app/init
This commit is contained in:
commit
0335fa66df
25 changed files with 1128 additions and 0 deletions
1
.flaskenv
Normal file
1
.flaskenv
Normal file
|
@ -0,0 +1 @@
|
||||||
|
FLASK_APP=BDSM
|
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
.DS_Store
|
||||||
|
.idea
|
||||||
|
*.log
|
||||||
|
tmp/
|
||||||
|
|
||||||
|
*.py[cod]
|
||||||
|
*.egg
|
||||||
|
build
|
||||||
|
htmlcov
|
||||||
|
|
||||||
|
*.secret
|
||||||
|
*.db
|
22
BDSM/__init__.py
Normal file
22
BDSM/__init__.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from flask import Flask, flash
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
|
# SQLite URI compatible
|
||||||
|
WIN = sys.platform.startswith('win')
|
||||||
|
if WIN:
|
||||||
|
prefix = 'sqlite:///'
|
||||||
|
else:
|
||||||
|
prefix = 'sqlite:////'
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev')
|
||||||
|
app.config['SQLALCHEMY_DATABASE_URI'] = prefix + os.path.join(os.path.dirname(app.root_path), os.getenv('DATABASE_FILE', 'data.db'))
|
||||||
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||||
|
|
||||||
|
db = SQLAlchemy(app)
|
||||||
|
|
||||||
|
from BDSM import views, models, commands
|
15
BDSM/commands.py
Normal file
15
BDSM/commands.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import click
|
||||||
|
|
||||||
|
from BDSM import app, db
|
||||||
|
from BDSM.models import Settings
|
||||||
|
|
||||||
|
|
||||||
|
@app.cli.command()
|
||||||
|
@click.option('--drop', is_flag=True, help='Create after drop.')
|
||||||
|
def initdb(drop):
|
||||||
|
"""Initialize the database."""
|
||||||
|
if drop:
|
||||||
|
db.drop_all()
|
||||||
|
db.create_all()
|
||||||
|
click.echo('Initialized database.')
|
82
BDSM/models.py
Normal file
82
BDSM/models.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from BDSM import db
|
||||||
|
|
||||||
|
class Toot(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
acct = db.Column(db.Text)
|
||||||
|
url = db.Column(db.Text)
|
||||||
|
created_at = db.Column(db.DateTime)
|
||||||
|
edited_at = db.Column(db.DateTime)
|
||||||
|
in_replay_to_id = db.Column(db.Integer)
|
||||||
|
in_replay_to_account_id = db.Column(db.Integer)
|
||||||
|
reblog_myself = db.Column(db.Boolean)
|
||||||
|
reblog_id = db.Column(db.Integer)
|
||||||
|
content = db.Column(db.Text)
|
||||||
|
media_list = db.Column(db.Text)
|
||||||
|
emoji_list = db.Column(db.Text)
|
||||||
|
spoiler_text = db.Column(db.Text)
|
||||||
|
poll_id = db.Column(db.Integer)
|
||||||
|
visibility = db.Column(db.Text)
|
||||||
|
reblogged = db.Column(db.Boolean)
|
||||||
|
favourited = db.Column(db.Boolean)
|
||||||
|
bookmarked = db.Column(db.Boolean)
|
||||||
|
sensitive = db.Column(db.Boolean)
|
||||||
|
replies_count = db.Column(db.Integer)
|
||||||
|
reblogs_count = db.Column(db.Integer)
|
||||||
|
favourites_count = db.Column(db.Integer)
|
||||||
|
language = db.Column(db.Text)
|
||||||
|
|
||||||
|
class Reblog(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
acct = db.Column(db.Text)
|
||||||
|
url = db.Column(db.Text)
|
||||||
|
created_at = db.Column(db.DateTime)
|
||||||
|
edited_at = db.Column(db.DateTime)
|
||||||
|
in_replay_to_id = db.Column(db.Integer)
|
||||||
|
in_replay_to_account_id = db.Column(db.Integer)
|
||||||
|
content = db.Column(db.Text)
|
||||||
|
media_list = db.Column(db.Text)
|
||||||
|
emoji_list = db.Column(db.Text)
|
||||||
|
spoiler_text = db.Column(db.Text)
|
||||||
|
poll_id = db.Column(db.Integer)
|
||||||
|
visibility = db.Column(db.Text)
|
||||||
|
reblogged = db.Column(db.Boolean)
|
||||||
|
favourited = db.Column(db.Boolean)
|
||||||
|
bookmarked = db.Column(db.Boolean)
|
||||||
|
sensitive = db.Column(db.Boolean)
|
||||||
|
replies_count = db.Column(db.Integer)
|
||||||
|
reblogs_count = db.Column(db.Integer)
|
||||||
|
favourites_count = db.Column(db.Integer)
|
||||||
|
language = db.Column(db.Text)
|
||||||
|
|
||||||
|
class Tag(db.Model):
|
||||||
|
__table_args__ = {'sqlite_autoincrement': True}
|
||||||
|
tag_id = db.Column(db.Integer, primary_key=True)
|
||||||
|
id = db.Column(db.Integer)
|
||||||
|
name = db.Column(db.Text)
|
||||||
|
|
||||||
|
class Emoji(db.Model):
|
||||||
|
shortcode = db.Column(db.Text, primary_key=True)
|
||||||
|
url = db.Column(db.Text)
|
||||||
|
static_url = db.Column(db.Text)
|
||||||
|
name = db.Column(db.Text)
|
||||||
|
count = db.Column(db.Integer)
|
||||||
|
|
||||||
|
class Media(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
type = db.Column(db.Text)
|
||||||
|
url = db.Column(db.Text)
|
||||||
|
remote_url = db.Column(db.Text)
|
||||||
|
description = db.Column(db.Text)
|
||||||
|
|
||||||
|
class Poll(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
expires_at = db.Column(db.DateTime)
|
||||||
|
multiple = db.Column(db.Boolean)
|
||||||
|
votes_count = db.Column(db.Integer)
|
||||||
|
options = db.Column(db.Text)
|
||||||
|
|
||||||
|
class Settings(db.Model):
|
||||||
|
account = db.Column(db.Text, primary_key=True)
|
||||||
|
timezone = db.Column(db.Text)
|
||||||
|
setup = db.Column(db.Boolean)
|
6
BDSM/static/css/all.min.css
vendored
Normal file
6
BDSM/static/css/all.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
7
BDSM/static/css/bootstrap.min.css
vendored
Normal file
7
BDSM/static/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
154
BDSM/static/css/style.css
Normal file
154
BDSM/static/css/style.css
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
body {
|
||||||
|
margin: auto !important;
|
||||||
|
padding: 5px;
|
||||||
|
max-width: 580px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toot {
|
||||||
|
padding-top: 10px;
|
||||||
|
border-top: 1px solid #393f4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta .time {
|
||||||
|
float: right;
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #606984;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visibility-icon .fa-globle {
|
||||||
|
color: #388E3C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visibility-icon .fa-unlock {
|
||||||
|
color: #1976D2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visibility-icon .fa-lock {
|
||||||
|
color: #FFA000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visibility-icon .fa-at {
|
||||||
|
color: #D32F2F;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-bar span {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination span {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
.icon-bar span {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li a {
|
||||||
|
display: block;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li a:hover {
|
||||||
|
background-color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
position: relative;
|
||||||
|
padding: 7px;
|
||||||
|
margin: 7px 0;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
color: #004085;
|
||||||
|
background-color: #cce5ff;
|
||||||
|
border-color: #b8daff;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit] {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[name=year] {
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invisible {
|
||||||
|
visibility: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-list {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-list li {
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-list li:last-child {
|
||||||
|
border-bottom:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.movie-list li:hover {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-form {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imdb {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: black;
|
||||||
|
text-decoration: none;
|
||||||
|
background: #F5C518;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 3px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.totoro {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
color: #888;
|
||||||
|
margin-top: 15px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
7
BDSM/static/js/bootstrap.bundle.min.js
vendored
Normal file
7
BDSM/static/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
BDSM/static/webfonts/fa-brands-400.ttf
Normal file
BIN
BDSM/static/webfonts/fa-brands-400.ttf
Normal file
Binary file not shown.
BIN
BDSM/static/webfonts/fa-brands-400.woff2
Normal file
BIN
BDSM/static/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
BDSM/static/webfonts/fa-regular-400.ttf
Normal file
BIN
BDSM/static/webfonts/fa-regular-400.ttf
Normal file
Binary file not shown.
BIN
BDSM/static/webfonts/fa-regular-400.woff2
Normal file
BIN
BDSM/static/webfonts/fa-regular-400.woff2
Normal file
Binary file not shown.
BIN
BDSM/static/webfonts/fa-solid-900.ttf
Normal file
BIN
BDSM/static/webfonts/fa-solid-900.ttf
Normal file
Binary file not shown.
BIN
BDSM/static/webfonts/fa-solid-900.woff2
Normal file
BIN
BDSM/static/webfonts/fa-solid-900.woff2
Normal file
Binary file not shown.
BIN
BDSM/static/webfonts/fa-v4compatibility.ttf
Normal file
BIN
BDSM/static/webfonts/fa-v4compatibility.ttf
Normal file
Binary file not shown.
BIN
BDSM/static/webfonts/fa-v4compatibility.woff2
Normal file
BIN
BDSM/static/webfonts/fa-v4compatibility.woff2
Normal file
Binary file not shown.
40
BDSM/templates/base.html
Normal file
40
BDSM/templates/base.html
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
{% block head %}
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
|
<title>Untitled</title>
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='/css/all.min.css') }}" type="text/css">
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='/css/bootstrap.min.css') }}" type="text/css">
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='/css/style.css') }}" type="text/css">
|
||||||
|
<script src="{{ url_for('static', filename='/js/bootstrap.bundle.min.js') }}"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||||
|
{% endblock %}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="row">
|
||||||
|
{% for message in get_flashed_messages() %}
|
||||||
|
<div class="alert">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{{ url_for('index') }}">主页</a></li>
|
||||||
|
<li><a href="{{ url_for('settings') }}">设置</a></li>
|
||||||
|
<li><a href="{{ url_for('archive') }}">存档</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
<footer>
|
||||||
|
<small><a href="https://git.southfox.me/southfox/mastodon-BDSM">Code</a></small>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
14
BDSM/templates/register.html
Normal file
14
BDSM/templates/register.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h3>授权</h3>
|
||||||
|
<p>Visit the following URL and authorize the app: </p>
|
||||||
|
<a href='{{ url }}' target="_blank">{{ url }}</a>
|
||||||
|
|
||||||
|
<p>Then paste the access token here:"</p>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
Token: <input type="text" name="token" autocomplete="off" required></br>
|
||||||
|
<input class="btn" type="submit" name="submit" value="提交">
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
49
BDSM/templates/settings.html
Normal file
49
BDSM/templates/settings.html
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h3>设置</h3>
|
||||||
|
<form method="post" class="form-horizontal" role="form">
|
||||||
|
{% if settings is defined %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-2 control-label">用户名</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" name="account" autocomplete="off" required
|
||||||
|
value="{{ settings.account }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-2 control-label">时区</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" name="timezone" autocomplete="off" required value="{{ settings.timezone }}"></br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<input class="btn btn-primary" type="submit" name="submit" value="保存">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-2 control-label">用户名</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" class="form-control" name="account" autocomplete="off" required value="@用户名@实例">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-2 control-label">时区</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input type="text" name="timezone" autocomplete="off" required value="Asia/Shanghai"></br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<input class="btn btn-primary" type="submit" name="submit" value="保存">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if not app_init %}
|
||||||
|
<p> 似乎你还没有进行认证,请到这个<a href='/register'>页面</a>进行授权。</p>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
87
BDSM/templates/view.html
Normal file
87
BDSM/templates/view.html
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% for toot in toots %}
|
||||||
|
<div class="toot">
|
||||||
|
<div class="status">
|
||||||
|
<div class="meta">
|
||||||
|
<strong><span class ="username">{{ toot.acct }}</span></strong>
|
||||||
|
<a href="{{ toot.url }}" target="_blank"
|
||||||
|
rel="noopener noreferrer">
|
||||||
|
<span class="time">
|
||||||
|
{% if toot. visibility == "public"%}
|
||||||
|
<span class="visibility-icon"><i class="fa-solid fa-earth-americas fa-globle"></i></span>
|
||||||
|
{% elif toot. visibility == "unlisted"%}
|
||||||
|
<span class="visibility-icon"><i class="fa-solid fa-lock-open fa-unlock"></i></span>
|
||||||
|
{% elif toot. visibility == "private"%}
|
||||||
|
<span class="visibility-icon"><i class="fa-solid fa-lock"></i></span>
|
||||||
|
{% elif toot. visibility == "direct"%}
|
||||||
|
<span class="visibility-icon"><i class="fa-solid fa-envelope fa-at"></i></span>
|
||||||
|
{% endif %}
|
||||||
|
<time>{{ toot.created_at }}</time></span></a>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
{{ toot.content|safe }}
|
||||||
|
</div>
|
||||||
|
<div class="icon-bar">
|
||||||
|
{% if toot.replies_count > 1 %}
|
||||||
|
<span><i class="fa-solid fa-reply-all"></i>{{ toot.replies_count }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span><i class="fa-solid fa-reply"></i>{{ toot.replies_count }}</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if toot.reblogged %}
|
||||||
|
<span><i class="fa-solid fa-arrow-rotate-right"></i>{{ toot.reblogs_count}}</span>
|
||||||
|
{% else %}
|
||||||
|
<span><i class="fa-solid fa-retweet"></i></i>{{ toot.reblogs_count}}</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if toot.favourited %}
|
||||||
|
<span><i class="fa-solid fa-star"></i>{{ toot.favourites_count }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span><i class="fa-regular fa-star"></i>{{ toot.favourites_count }}</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if toot.bookmarked %}
|
||||||
|
<span><i class="fa-solid fa-bookmark"></i></span>
|
||||||
|
{% else %}
|
||||||
|
<span><i class="fa-regular fa-bookmark"></i></span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="pagination d-flex justify-content-center">
|
||||||
|
{% if pagination.has_prev %}
|
||||||
|
<span>
|
||||||
|
<a class='page-number' href="{{ url_for('index', page=pagination.prev_num) }}">
|
||||||
|
{{ '<<<' }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for number in pagination.iter_pages() %}
|
||||||
|
{% if number == None %}
|
||||||
|
<span>...</span>
|
||||||
|
{% elif pagination.page != number %}
|
||||||
|
<span>
|
||||||
|
<a class='page-number'
|
||||||
|
href="{{ url_for('index', page=number) }}">
|
||||||
|
{{ number }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
<span class='current-page-number'>{{ number }}</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if pagination.has_next %}
|
||||||
|
<span>
|
||||||
|
<a class='page-number' href="{{ url_for('index', page=pagination.next_num) }}">
|
||||||
|
{{ '>>>' }}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
191
BDSM/toot.py
Normal file
191
BDSM/toot.py
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from mastodon import Mastodon
|
||||||
|
from BDSM import db
|
||||||
|
from BDSM.models import Reblog, Toot, Tag, Media, Emoji, Poll
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def app_register(url):
|
||||||
|
print("Registering app")
|
||||||
|
Mastodon.create_app(
|
||||||
|
'pyBDSM',
|
||||||
|
api_base_url = url,
|
||||||
|
to_file = 'pyBDSM_clientcred.secret',
|
||||||
|
scopes=["read"]
|
||||||
|
)
|
||||||
|
|
||||||
|
def archive_toot(url):
|
||||||
|
mastodon = Mastodon(
|
||||||
|
client_id='pyBDSM_clientcred.secret',
|
||||||
|
access_token='user.secret',
|
||||||
|
api_base_url=url
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = mastodon.account_verify_credentials()
|
||||||
|
except Exception as e:
|
||||||
|
if "access token was revoked" in str(e):
|
||||||
|
print("revoked token")
|
||||||
|
sys.exit(0)
|
||||||
|
elif "Name or service not known" in str(e):
|
||||||
|
print("Error: the instance name is either misspelled or offline",
|
||||||
|
file=sys.stderr)
|
||||||
|
else:
|
||||||
|
print(e, file=sys.stderr)
|
||||||
|
# exit in either case
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
acct = mastodon.me().acct
|
||||||
|
statuses_count = str(mastodon.me().statuses_count)
|
||||||
|
|
||||||
|
statuses = mastodon.account_statuses(user["id"], limit=20)
|
||||||
|
# xx = statuses['created_at'].astimezone(tz_cn)
|
||||||
|
# pprint(xx.strftime("%m/%d/%Y, %H:%M:%S"))
|
||||||
|
# pprint(statuses)
|
||||||
|
|
||||||
|
happy_counter = 20
|
||||||
|
|
||||||
|
while(True):
|
||||||
|
for status in statuses:
|
||||||
|
is_reblog = False
|
||||||
|
if status['reblog'] != None:
|
||||||
|
if acct == status['reblog']['account']['acct']:
|
||||||
|
reblog_myself = True
|
||||||
|
else:
|
||||||
|
reblog_myself = False
|
||||||
|
|
||||||
|
is_reblog = True
|
||||||
|
|
||||||
|
reblog_id = status['reblog']['id']
|
||||||
|
id = status['id']
|
||||||
|
created_at = status['created_at']
|
||||||
|
|
||||||
|
toot = Toot(id=id, created_at=created_at, reblog_myself=reblog_myself, reblog_id=reblog_id)
|
||||||
|
db.session.add(toot)
|
||||||
|
# cur.execute('''INSERT OR REPLACE INTO TOOT (id,created_at,reblog_myself,reblog_id) \
|
||||||
|
# VALUES (?,?,?,?)''',(id, created_at, reblog_myself, reblog_id))
|
||||||
|
|
||||||
|
if reblog_myself:
|
||||||
|
continue
|
||||||
|
|
||||||
|
status = status['reblog']
|
||||||
|
|
||||||
|
id = status['id']
|
||||||
|
acct = status['account']['acct']
|
||||||
|
url = status['url']
|
||||||
|
created_at = status['created_at']
|
||||||
|
edited_at = status['edited_at'] if status['edited_at'] != None else None
|
||||||
|
in_reply_to_id = status['in_reply_to_id']
|
||||||
|
in_reply_to_account_id = status['in_reply_to_account_id']
|
||||||
|
content = status['content']
|
||||||
|
|
||||||
|
if status['media_attachments'] != []:
|
||||||
|
media_list = []
|
||||||
|
for media_dict in status['media_attachments']:
|
||||||
|
media_list.append(media_dict['id'])
|
||||||
|
|
||||||
|
media = Media(id=media_dict['id'], type=media_dict['type'], url=media_dict['url'],
|
||||||
|
remote_url=media_dict['remote_url'], description=media_dict['description'])
|
||||||
|
db.session.add(media)
|
||||||
|
# cur.execute('''INSERT OR REPLACE INTO MEDIA (id,type,url,remote_url,description) \
|
||||||
|
# VALUES (?,?,?,?,?)''',(media_dict['id'], media_dict['type'], media_dict['url'], \
|
||||||
|
# media_dict['remote_url'], media_dict['description']))
|
||||||
|
else:
|
||||||
|
media_list = []
|
||||||
|
media_list = str(media_list)
|
||||||
|
|
||||||
|
spoiler_text = status['spoiler_text']
|
||||||
|
|
||||||
|
if status['poll'] != None:
|
||||||
|
poll_dict = status['poll']
|
||||||
|
poll_id = poll_dict['id']
|
||||||
|
expires_at = poll_dict['expires_at']
|
||||||
|
options = str(poll_dict['options'])
|
||||||
|
|
||||||
|
poll = Poll(id=poll_dict['id'], expires_at=expires_at, multiple=poll_dict['multiple'], \
|
||||||
|
votes_count=poll_dict['votes_count'], options=options)
|
||||||
|
db.session.add(poll)
|
||||||
|
# cur.execute('''INSERT OR REPLACE INTO POLL (id,expires_at,multiple,votes_count,options) \
|
||||||
|
# VALUES (?,?,?,?,?)''',(poll_dict['id'], expires_at, poll_dict['multiple'], \
|
||||||
|
# poll_dict['votes_count'], options))
|
||||||
|
else:
|
||||||
|
poll_id = None
|
||||||
|
|
||||||
|
if status['emojis'] != []:
|
||||||
|
emoji_list = []
|
||||||
|
for emoji in status['emojis']:
|
||||||
|
shortcode = emoji['shortcode']
|
||||||
|
emoji_list.append(shortcode)
|
||||||
|
counter = ':' + shortcode + ':'
|
||||||
|
count = content.count(counter)
|
||||||
|
|
||||||
|
data=Emoji.query.filter_by(shortcode=shortcode).first()
|
||||||
|
if data is None:
|
||||||
|
emoji_data = Emoji(shortcode=shortcode, url=emoji['url'], static_url=emoji['static_url'], count=count)
|
||||||
|
db.session.add(emoji_data)
|
||||||
|
# cur.execute('''INSERT INTO EMOJI (shortcode,url,static_url,count) \
|
||||||
|
# VALUES (?,?,?,?)''', (shortcode, emoji['url'], emoji['static_url'], count))
|
||||||
|
else:
|
||||||
|
data.count += count
|
||||||
|
# cur.execute("UPDATE EMOJI SET count = ? WHERE shortcode = ?",(count, shortcode))
|
||||||
|
else:
|
||||||
|
emoji_list = []
|
||||||
|
emoji_list = str(emoji_list)
|
||||||
|
|
||||||
|
if status['tags'] != []:
|
||||||
|
for tag in status['tags']:
|
||||||
|
tag_data = Tag(id=id, name=tag['name'])
|
||||||
|
db.session.add(tag_data)
|
||||||
|
# cur.execute('''INSERT OR REPLACE INTO TAG (id,name) \
|
||||||
|
# VALUES (?,?)''',(id, tag['name']))
|
||||||
|
|
||||||
|
visibility = status['visibility']
|
||||||
|
reblogged = status['reblogged']
|
||||||
|
favourited = status['favourited']
|
||||||
|
bookmarked = status['bookmarked']
|
||||||
|
sensitive = status['sensitive']
|
||||||
|
replies_count = status['replies_count']
|
||||||
|
reblogs_count = status['reblogs_count']
|
||||||
|
favourites_count = status['favourites_count']
|
||||||
|
language = status['language']
|
||||||
|
|
||||||
|
table = Reblog() if is_reblog else Toot()
|
||||||
|
table.id=id
|
||||||
|
table.acct = acct
|
||||||
|
table.url=url
|
||||||
|
table.created_at=created_at
|
||||||
|
table.edited_at=edited_at
|
||||||
|
table.in_reply_to_id=in_reply_to_id
|
||||||
|
table.in_reply_to_account_id=in_reply_to_account_id
|
||||||
|
table.content=content
|
||||||
|
table.media_list=media_list
|
||||||
|
table.spoiler_text=spoiler_text
|
||||||
|
table.poll_id=poll_id
|
||||||
|
table.emoji_list=emoji_list
|
||||||
|
table.visibility=visibility
|
||||||
|
table.reblogged=reblogged
|
||||||
|
table.favourited=favourited
|
||||||
|
table.bookmarked=bookmarked
|
||||||
|
table.sensitive=sensitive
|
||||||
|
table.replies_count=replies_count
|
||||||
|
table.reblogs_count=reblogs_count
|
||||||
|
table.favourites_count=favourites_count
|
||||||
|
table.language=language
|
||||||
|
|
||||||
|
|
||||||
|
db.session.add(table)
|
||||||
|
# sql = f'''INSERT OR REPLACE INTO {table} (id,url,created_at,edited_at,in_reply_to_id,in_reply_to_account_id,content,\
|
||||||
|
# media_list,spoiler_text,poll_id,emoji_list,visibility,reblogged,favourited,bookmarked,sensitive,reblogs_count,\
|
||||||
|
# favourites_count,language) \
|
||||||
|
# VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)'''
|
||||||
|
# cur.execute(sql,(id,url,created_at,edited_at,in_reply_to_id,in_reply_to_account_id,content,media_list,spoiler_text,\
|
||||||
|
# poll_id,emoji_list,visibility,reblogged,favourited,bookmarked,sensitive,reblogs_count,favourites_count,language))
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
print(str(happy_counter) + ' / ' + statuses_count)
|
||||||
|
happy_counter += 20
|
||||||
|
|
||||||
|
statuses = mastodon.fetch_next(statuses)
|
||||||
|
# statuses = None
|
||||||
|
if statuses == None:
|
||||||
|
break
|
102
BDSM/views.py
Normal file
102
BDSM/views.py
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
|
||||||
|
from flask import render_template, request, url_for, redirect, flash
|
||||||
|
from BDSM import app, db
|
||||||
|
from BDSM.models import Settings, Toot
|
||||||
|
from BDSM.toot import app_register, archive_toot
|
||||||
|
from mastodon import Mastodon
|
||||||
|
|
||||||
|
@app.route('/', methods=['GET', 'POST'])
|
||||||
|
def index():
|
||||||
|
page = request.args.get('page', 1, type=int)
|
||||||
|
toots_ = Toot.query.order_by(Toot.created_at.desc()).paginate(page, per_page=50)
|
||||||
|
toots = []
|
||||||
|
|
||||||
|
for toot in toots_.items:
|
||||||
|
if toot.content == None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
toots.append(toot)
|
||||||
|
|
||||||
|
return render_template('view.html', toots=toots, pagination=toots_)
|
||||||
|
|
||||||
|
@app.route('/settings', methods=['GET', 'POST'])
|
||||||
|
def settings():
|
||||||
|
if request.method == 'POST':
|
||||||
|
account = request.form['account']
|
||||||
|
timezone = request.form['timezone']
|
||||||
|
|
||||||
|
if not account or len(account) > 30:
|
||||||
|
flash('无效输入')
|
||||||
|
return redirect(url_for('settings'))
|
||||||
|
|
||||||
|
settings = Settings.query.first()
|
||||||
|
|
||||||
|
if settings == None:
|
||||||
|
settings = Settings(account=account, timezone=timezone)
|
||||||
|
db.session.add(settings)
|
||||||
|
else:
|
||||||
|
settings.account = account
|
||||||
|
settings.timezone = timezone
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
flash('设置已修改')
|
||||||
|
return redirect(url_for('settings'))
|
||||||
|
|
||||||
|
settings = Settings.query.first()
|
||||||
|
if settings == None:
|
||||||
|
flash('请输入用户名')
|
||||||
|
return render_template('settings.html')
|
||||||
|
|
||||||
|
app_init = os.path.isfile('pyBDSM_clientcred.secret') and os.path.isfile('user.secret')
|
||||||
|
|
||||||
|
return render_template('settings.html',settings=settings, app_init=app_init)
|
||||||
|
|
||||||
|
@app.route('/register', methods=['GET', 'POST'])
|
||||||
|
def register():
|
||||||
|
settings = Settings.query.first()
|
||||||
|
if settings == None:
|
||||||
|
flash('请先输入用户名!')
|
||||||
|
return redirect(url_for('settings'))
|
||||||
|
else:
|
||||||
|
account = settings.account[1:]
|
||||||
|
username, domain = account.split("@")
|
||||||
|
url = "https://" + domain
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
token = request.form['token'].rstrip()
|
||||||
|
mastodon = Mastodon(client_id='pyBDSM_clientcred.secret', api_base_url=url)
|
||||||
|
mastodon.log_in(code=token, to_file='user.secret', scopes=['read'])
|
||||||
|
|
||||||
|
if os.path.isfile('user.secret'):
|
||||||
|
flash('应用已授权!')
|
||||||
|
return redirect(url_for('settings'))
|
||||||
|
|
||||||
|
if not os.path.isfile('pyBDSM_clientcred.secret'):
|
||||||
|
app_register(url)
|
||||||
|
if not os.path.isfile('user.secret'):
|
||||||
|
mastodon = Mastodon(client_id='pyBDSM_clientcred.secret', api_base_url=url)
|
||||||
|
url = mastodon.auth_request_url(client_id='pyBDSM_clientcred.secret', scopes=['read'])
|
||||||
|
return render_template('register.html',url=url)
|
||||||
|
|
||||||
|
flash('已授权过!')
|
||||||
|
return redirect(url_for('settings'))
|
||||||
|
|
||||||
|
@app.route('/archive', methods=['GET', 'POST'])
|
||||||
|
def archive():
|
||||||
|
settings = Settings.query.first()
|
||||||
|
if settings == None:
|
||||||
|
return redirect(url_for('settings'))
|
||||||
|
elif len(Toot.query.all()) > 0:
|
||||||
|
flash('现暂不支持重复存档!') #TODO
|
||||||
|
return redirect(url_for('index'))
|
||||||
|
else:
|
||||||
|
account = settings.account[1:]
|
||||||
|
username, domain = account.split("@")
|
||||||
|
url = "https://" + domain
|
||||||
|
|
||||||
|
archive_toot(url)
|
||||||
|
|
||||||
|
flash('存档完成……大概!')
|
||||||
|
return redirect(url_for('index'))
|
15
Pipfile
Normal file
15
Pipfile
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
"mastodon.py" = "*"
|
||||||
|
flask = "*"
|
||||||
|
flask-sqlalchemy = "*"
|
||||||
|
python-dotenv = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.10"
|
324
Pipfile.lock
generated
Normal file
324
Pipfile.lock
generated
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "5c49bdfaa2bb333c4af5bc5fa02d920a87c2d429460948ade1295a11dbac6a95"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3.10"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"blurhash": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7611c1bc41383d2349b6129208587b5d61e8792ce953893cb49c38beeb400d1d",
|
||||||
|
"sha256:da56b163e5a816e4ad07172f5639287698e09d7f3dc38d18d9726d9c1dbc4cee"
|
||||||
|
],
|
||||||
|
"version": "==1.1.4"
|
||||||
|
},
|
||||||
|
"certifi": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:36973885b9542e6bd01dea287b2b4b3b21236307c56324fcc3f1160f2d655ed5",
|
||||||
|
"sha256:e232343de1ab72c2aa521b625c80f699e356830fd0e2c620b465b304b17b0516"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==2022.9.14"
|
||||||
|
},
|
||||||
|
"charset-normalizer": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
|
||||||
|
"sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==2.1.1"
|
||||||
|
},
|
||||||
|
"click": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
||||||
|
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==8.1.3"
|
||||||
|
},
|
||||||
|
"decorator": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330",
|
||||||
|
"sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.5'",
|
||||||
|
"version": "==5.1.1"
|
||||||
|
},
|
||||||
|
"flask": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b",
|
||||||
|
"sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==2.2.2"
|
||||||
|
},
|
||||||
|
"flask-sqlalchemy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2bda44b43e7cacb15d4e05ff3cc1f8bc97936cc464623424102bfc2c35e95912",
|
||||||
|
"sha256:f12c3d4cc5cc7fdcc148b9527ea05671718c3ea45d50c7e732cceb33f574b390"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==2.5.1"
|
||||||
|
},
|
||||||
|
"greenlet": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0118817c9341ef2b0f75f5af79ac377e4da6ff637e5ee4ac91802c0e379dadb4",
|
||||||
|
"sha256:048d2bed76c2aa6de7af500ae0ea51dd2267aec0e0f2a436981159053d0bc7cc",
|
||||||
|
"sha256:07c58e169bbe1e87b8bbf15a5c1b779a7616df9fd3e61cadc9d691740015b4f8",
|
||||||
|
"sha256:095a980288fe05adf3d002fbb180c99bdcf0f930e220aa66fcd56e7914a38202",
|
||||||
|
"sha256:0b181e9aa6cb2f5ec0cacc8cee6e5a3093416c841ba32c185c30c160487f0380",
|
||||||
|
"sha256:1626185d938d7381631e48e6f7713e8d4b964be246073e1a1d15c2f061ac9f08",
|
||||||
|
"sha256:184416e481295832350a4bf731ba619a92f5689bf5d0fa4341e98b98b1265bd7",
|
||||||
|
"sha256:1dd51d2650e70c6c4af37f454737bf4a11e568945b27f74b471e8e2a9fd21268",
|
||||||
|
"sha256:1ec2779774d8e42ed0440cf8bc55540175187e8e934f2be25199bf4ed948cd9e",
|
||||||
|
"sha256:2cf45e339cabea16c07586306a31cfcc5a3b5e1626d365714d283732afed6809",
|
||||||
|
"sha256:2fb0aa7f6996879551fd67461d5d3ab0c3c0245da98be90c89fcb7a18d437403",
|
||||||
|
"sha256:44b4817c34c9272c65550b788913620f1fdc80362b209bc9d7dd2f40d8793080",
|
||||||
|
"sha256:466ce0928e33421ee84ae04c4ac6f253a3a3e6b8d600a79bd43fd4403e0a7a76",
|
||||||
|
"sha256:4f166b4aca8d7d489e82d74627a7069ab34211ef5ebb57c300ec4b9337b60fc0",
|
||||||
|
"sha256:510c3b15587afce9800198b4b142202b323bf4b4b5f9d6c79cb9a35e5e3c30d2",
|
||||||
|
"sha256:5b756e6730ea59b2745072e28ad27f4c837084688e6a6b3633c8b1e509e6ae0e",
|
||||||
|
"sha256:5fbe1ab72b998ca77ceabbae63a9b2e2dc2d963f4299b9b278252ddba142d3f1",
|
||||||
|
"sha256:6200a11f003ec26815f7e3d2ded01b43a3810be3528dd760d2f1fa777490c3cd",
|
||||||
|
"sha256:65ad1a7a463a2a6f863661329a944a5802c7129f7ad33583dcc11069c17e622c",
|
||||||
|
"sha256:694ffa7144fa5cc526c8f4512665003a39fa09ef00d19bbca5c8d3406db72fbe",
|
||||||
|
"sha256:6f5d4b2280ceea76c55c893827961ed0a6eadd5a584a7c4e6e6dd7bc10dfdd96",
|
||||||
|
"sha256:7532a46505470be30cbf1dbadb20379fb481244f1ca54207d7df3bf0bbab6a20",
|
||||||
|
"sha256:76a53bfa10b367ee734b95988bd82a9a5f0038a25030f9f23bbbc005010ca600",
|
||||||
|
"sha256:77e41db75f9958f2083e03e9dd39da12247b3430c92267df3af77c83d8ff9eed",
|
||||||
|
"sha256:7a43bbfa9b6cfdfaeefbd91038dde65ea2c421dc387ed171613df340650874f2",
|
||||||
|
"sha256:7b41d19c0cfe5c259fe6c539fd75051cd39a5d33d05482f885faf43f7f5e7d26",
|
||||||
|
"sha256:7c5227963409551ae4a6938beb70d56bf1918c554a287d3da6853526212fbe0a",
|
||||||
|
"sha256:870a48007872d12e95a996fca3c03a64290d3ea2e61076aa35d3b253cf34cd32",
|
||||||
|
"sha256:88b04e12c9b041a1e0bcb886fec709c488192638a9a7a3677513ac6ba81d8e79",
|
||||||
|
"sha256:8c287ae7ac921dfde88b1c125bd9590b7ec3c900c2d3db5197f1286e144e712b",
|
||||||
|
"sha256:903fa5716b8fbb21019268b44f73f3748c41d1a30d71b4a49c84b642c2fed5fa",
|
||||||
|
"sha256:9537e4baf0db67f382eb29255a03154fcd4984638303ff9baaa738b10371fa57",
|
||||||
|
"sha256:9951dcbd37850da32b2cb6e391f621c1ee456191c6ae5528af4a34afe357c30e",
|
||||||
|
"sha256:9b2f7d0408ddeb8ea1fd43d3db79a8cefaccadd2a812f021333b338ed6b10aba",
|
||||||
|
"sha256:9c88e134d51d5e82315a7c32b914a58751b7353eb5268dbd02eabf020b4c4700",
|
||||||
|
"sha256:9fae214f6c43cd47f7bef98c56919b9222481e833be2915f6857a1e9e8a15318",
|
||||||
|
"sha256:a3a669f11289a8995d24fbfc0e63f8289dd03c9aaa0cc8f1eab31d18ca61a382",
|
||||||
|
"sha256:aa741c1a8a8cc25eb3a3a01a62bdb5095a773d8c6a86470bde7f607a447e7905",
|
||||||
|
"sha256:b0877a9a2129a2c56a2eae2da016743db7d9d6a05d5e1c198f1b7808c602a30e",
|
||||||
|
"sha256:bcb6c6dd1d6be6d38d6db283747d07fda089ff8c559a835236560a4410340455",
|
||||||
|
"sha256:caff52cb5cd7626872d9696aee5b794abe172804beb7db52eed1fd5824b63910",
|
||||||
|
"sha256:cbc1eb55342cbac8f7ec159088d54e2cfdd5ddf61c87b8bbe682d113789331b2",
|
||||||
|
"sha256:cd16a89efe3a003029c87ff19e9fba635864e064da646bc749fc1908a4af18f3",
|
||||||
|
"sha256:ce5b64dfe8d0cca407d88b0ee619d80d4215a2612c1af8c98a92180e7109f4b5",
|
||||||
|
"sha256:d58a5a71c4c37354f9e0c24c9c8321f0185f6945ef027460b809f4bb474bfe41",
|
||||||
|
"sha256:db41f3845eb579b544c962864cce2c2a0257fe30f0f1e18e51b1e8cbb4e0ac6d",
|
||||||
|
"sha256:db5b25265010a1b3dca6a174a443a0ed4c4ab12d5e2883a11c97d6e6d59b12f9",
|
||||||
|
"sha256:dd0404d154084a371e6d2bafc787201612a1359c2dee688ae334f9118aa0bf47",
|
||||||
|
"sha256:de431765bd5fe62119e0bc6bc6e7b17ac53017ae1782acf88fcf6b7eae475a49",
|
||||||
|
"sha256:df02fdec0c533301497acb0bc0f27f479a3a63dcdc3a099ae33a902857f07477",
|
||||||
|
"sha256:e8533f5111704d75de3139bf0b8136d3a6c1642c55c067866fa0a51c2155ee33",
|
||||||
|
"sha256:f2f908239b7098799b8845e5936c2ccb91d8c2323be02e82f8dcb4a80dcf4a25",
|
||||||
|
"sha256:f8bfd36f368efe0ab2a6aa3db7f14598aac454b06849fb633b762ddbede1db90",
|
||||||
|
"sha256:ffe73f9e7aea404722058405ff24041e59d31ca23d1da0895af48050a07b6932"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))",
|
||||||
|
"version": "==1.1.3"
|
||||||
|
},
|
||||||
|
"idna": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
|
||||||
|
"sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.5'",
|
||||||
|
"version": "==3.4"
|
||||||
|
},
|
||||||
|
"itsdangerous": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44",
|
||||||
|
"sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==2.1.2"
|
||||||
|
},
|
||||||
|
"jinja2": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
|
||||||
|
"sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==3.1.2"
|
||||||
|
},
|
||||||
|
"markupsafe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
|
||||||
|
"sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88",
|
||||||
|
"sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5",
|
||||||
|
"sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7",
|
||||||
|
"sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a",
|
||||||
|
"sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603",
|
||||||
|
"sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1",
|
||||||
|
"sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135",
|
||||||
|
"sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247",
|
||||||
|
"sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6",
|
||||||
|
"sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601",
|
||||||
|
"sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77",
|
||||||
|
"sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02",
|
||||||
|
"sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e",
|
||||||
|
"sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63",
|
||||||
|
"sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f",
|
||||||
|
"sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980",
|
||||||
|
"sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b",
|
||||||
|
"sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812",
|
||||||
|
"sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff",
|
||||||
|
"sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96",
|
||||||
|
"sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1",
|
||||||
|
"sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925",
|
||||||
|
"sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a",
|
||||||
|
"sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6",
|
||||||
|
"sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e",
|
||||||
|
"sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f",
|
||||||
|
"sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4",
|
||||||
|
"sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f",
|
||||||
|
"sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3",
|
||||||
|
"sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c",
|
||||||
|
"sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a",
|
||||||
|
"sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417",
|
||||||
|
"sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a",
|
||||||
|
"sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a",
|
||||||
|
"sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37",
|
||||||
|
"sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452",
|
||||||
|
"sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933",
|
||||||
|
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
|
||||||
|
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==2.1.1"
|
||||||
|
},
|
||||||
|
"mastodon.py": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2afddbad8b5d7326fcc8a8f8c62bfe956e34627f516b06c6694fc8c8fedc33ee",
|
||||||
|
"sha256:cc454cac0ed1ae4f105f7399ea53f5b31a1be5075d1882f47162d2e78a9e4064"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.5.1"
|
||||||
|
},
|
||||||
|
"python-dateutil": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
|
||||||
|
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||||
|
"version": "==2.8.2"
|
||||||
|
},
|
||||||
|
"python-dotenv": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5",
|
||||||
|
"sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.21.0"
|
||||||
|
},
|
||||||
|
"python-magic": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b",
|
||||||
|
"sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||||
|
"version": "==0.4.27"
|
||||||
|
},
|
||||||
|
"pytz": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197",
|
||||||
|
"sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"
|
||||||
|
],
|
||||||
|
"version": "==2022.2.1"
|
||||||
|
},
|
||||||
|
"requests": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983",
|
||||||
|
"sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7' and python_version < '4'",
|
||||||
|
"version": "==2.28.1"
|
||||||
|
},
|
||||||
|
"six": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||||
|
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||||
|
"version": "==1.16.0"
|
||||||
|
},
|
||||||
|
"sqlalchemy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0002e829142b2af00b4eaa26c51728f3ea68235f232a2e72a9508a3116bd6ed0",
|
||||||
|
"sha256:0005bd73026cd239fc1e8ccdf54db58b6193be9a02b3f0c5983808f84862c767",
|
||||||
|
"sha256:0292f70d1797e3c54e862e6f30ae474014648bc9c723e14a2fda730adb0a9791",
|
||||||
|
"sha256:036d8472356e1d5f096c5e0e1a7e0f9182140ada3602f8fff6b7329e9e7cfbcd",
|
||||||
|
"sha256:05f0de3a1dc3810a776275763764bb0015a02ae0f698a794646ebc5fb06fad33",
|
||||||
|
"sha256:0990932f7cca97fece8017414f57fdd80db506a045869d7ddf2dda1d7cf69ecc",
|
||||||
|
"sha256:13e397a9371ecd25573a7b90bd037db604331cf403f5318038c46ee44908c44d",
|
||||||
|
"sha256:14576238a5f89bcf504c5f0a388d0ca78df61fb42cb2af0efe239dc965d4f5c9",
|
||||||
|
"sha256:199a73c31ac8ea59937cc0bf3dfc04392e81afe2ec8a74f26f489d268867846c",
|
||||||
|
"sha256:2082a2d2fca363a3ce21cfa3d068c5a1ce4bf720cf6497fb3a9fc643a8ee4ddd",
|
||||||
|
"sha256:22ff16cedab5b16a0db79f1bc99e46a6ddececb60c396562e50aab58ddb2871c",
|
||||||
|
"sha256:2307495d9e0ea00d0c726be97a5b96615035854972cc538f6e7eaed23a35886c",
|
||||||
|
"sha256:2ad2b727fc41c7f8757098903f85fafb4bf587ca6605f82d9bf5604bd9c7cded",
|
||||||
|
"sha256:2d6495f84c4fd11584f34e62f9feec81bf373787b3942270487074e35cbe5330",
|
||||||
|
"sha256:361f6b5e3f659e3c56ea3518cf85fbdae1b9e788ade0219a67eeaaea8a4e4d2a",
|
||||||
|
"sha256:3e2ef592ac3693c65210f8b53d0edcf9f4405925adcfc031ff495e8d18169682",
|
||||||
|
"sha256:4676d51c9f6f6226ae8f26dc83ec291c088fe7633269757d333978df78d931ab",
|
||||||
|
"sha256:4ba7e122510bbc07258dc42be6ed45997efdf38129bde3e3f12649be70683546",
|
||||||
|
"sha256:5102fb9ee2c258a2218281adcb3e1918b793c51d6c2b4666ce38c35101bb940e",
|
||||||
|
"sha256:5323252be2bd261e0aa3f33cb3a64c45d76829989fa3ce90652838397d84197d",
|
||||||
|
"sha256:58bb65b3274b0c8a02cea9f91d6f44d0da79abc993b33bdedbfec98c8440175a",
|
||||||
|
"sha256:59bdc291165b6119fc6cdbc287c36f7f2859e6051dd923bdf47b4c55fd2f8bd0",
|
||||||
|
"sha256:5facb7fd6fa8a7353bbe88b95695e555338fb038ad19ceb29c82d94f62775a05",
|
||||||
|
"sha256:639e1ae8d48b3c86ffe59c0daa9a02e2bfe17ca3d2b41611b30a0073937d4497",
|
||||||
|
"sha256:8eb8897367a21b578b26f5713833836f886817ee2ffba1177d446fa3f77e67c8",
|
||||||
|
"sha256:90484a2b00baedad361402c257895b13faa3f01780f18f4a104a2f5c413e4536",
|
||||||
|
"sha256:9c56e19780cd1344fcd362fd6265a15f48aa8d365996a37fab1495cae8fcd97d",
|
||||||
|
"sha256:b67fc780cfe2b306180e56daaa411dd3186bf979d50a6a7c2a5b5036575cbdbb",
|
||||||
|
"sha256:c0dcf127bb99458a9d211e6e1f0f3edb96c874dd12f2503d4d8e4f1fd103790b",
|
||||||
|
"sha256:c23d64a0b28fc78c96289ffbd0d9d1abd48d267269b27f2d34e430ea73ce4b26",
|
||||||
|
"sha256:ccfd238f766a5bb5ee5545a62dd03f316ac67966a6a658efb63eeff8158a4bbf",
|
||||||
|
"sha256:cd767cf5d7252b1c88fcfb58426a32d7bd14a7e4942497e15b68ff5d822b41ad",
|
||||||
|
"sha256:ce8feaa52c1640de9541eeaaa8b5fb632d9d66249c947bb0d89dd01f87c7c288",
|
||||||
|
"sha256:d2e054aed4645f9b755db85bc69fc4ed2c9020c19c8027976f66576b906a74f1",
|
||||||
|
"sha256:e16c2be5cb19e2c08da7bd3a87fed2a0d4e90065ee553a940c4fc1a0fb1ab72b",
|
||||||
|
"sha256:e4b12e3d88a8fffd0b4ca559f6d4957ed91bd4c0613a4e13846ab8729dc5c251",
|
||||||
|
"sha256:e570cfc40a29d6ad46c9aeaddbdcee687880940a3a327f2c668dd0e4ef0a441d",
|
||||||
|
"sha256:eb30cf008850c0a26b72bd1b9be6730830165ce049d239cfdccd906f2685f892",
|
||||||
|
"sha256:f37fa70d95658763254941ddd30ecb23fc4ec0c5a788a7c21034fc2305dab7cc",
|
||||||
|
"sha256:f5ebeeec5c14533221eb30bad716bc1fd32f509196318fb9caa7002c4a364e4c",
|
||||||
|
"sha256:f5fa526d027d804b1f85cdda1eb091f70bde6fb7d87892f6dd5a48925bc88898"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||||
|
"version": "==1.4.41"
|
||||||
|
},
|
||||||
|
"urllib3": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e",
|
||||||
|
"sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
|
||||||
|
"version": "==1.26.12"
|
||||||
|
},
|
||||||
|
"werkzeug": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f",
|
||||||
|
"sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==2.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {}
|
||||||
|
}
|
Loading…
Reference in a new issue