Compare commits

...

5 commits

Author SHA1 Message Date
f164ddaa22 fix/edited_at compatible with lower versions 2022-11-08 02:29:56 +08:00
670e7d42ba feat/show other account replys 2022-11-08 02:25:06 +08:00
6793fda0c8 feat/show if edited 2022-11-07 23:58:20 +08:00
3eae1f36ce feat/support incremental backup
fix/fix edited_at typeerror
2022-11-07 23:49:10 +08:00
7fcff2566a db/change db model name 2022-11-07 22:42:20 +08:00
4 changed files with 237 additions and 170 deletions

View file

@ -26,7 +26,7 @@ class Toot(db.Model):
favourites_count = db.Column(db.Integer) favourites_count = db.Column(db.Integer)
language = db.Column(db.Text) language = db.Column(db.Text)
class Reblog(db.Model): class Other(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
acct = db.Column(db.Text) acct = db.Column(db.Text)
url = db.Column(db.Text) url = db.Column(db.Text)

View file

@ -12,16 +12,20 @@
<a href="{{ toot.url }}" target="_blank" <a href="{{ toot.url }}" target="_blank"
rel="noopener noreferrer"> rel="noopener noreferrer">
<span class="time"> <span class="time">
{% if toot. visibility == "public"%} {% if toot.visibility == "public"%}
<span class="visibility-icon"><i class="fa-solid fa-earth-americas fa-globle"></i></span> <span class="visibility-icon"><i class="fa-solid fa-earth-americas fa-globle"></i></span>
{% elif toot. visibility == "unlisted"%} {% elif toot.visibility == "unlisted"%}
<span class="visibility-icon"><i class="fa-solid fa-lock-open fa-unlock"></i></span> <span class="visibility-icon"><i class="fa-solid fa-lock-open fa-unlock"></i></span>
{% elif toot. visibility == "private"%} {% elif toot.visibility == "private"%}
<span class="visibility-icon"><i class="fa-solid fa-lock"></i></span> <span class="visibility-icon"><i class="fa-solid fa-lock"></i></span>
{% elif toot. visibility == "direct"%} {% elif toot.visibility == "direct"%}
<span class="visibility-icon"><i class="fa-solid fa-envelope fa-at"></i></span> <span class="visibility-icon"><i class="fa-solid fa-envelope fa-at"></i></span>
{% endif %} {% endif %}
<time>{{ toot.created_at }}</time></span></a> <time>{{ toot.created_at }}
{% if toot.edited_at != None %}
<strong>*</strong>
{% endif %}
</time></span></a>
</div> </div>
<div class="content"> <div class="content">
{% if toot.spoiler_text != "" %} {% if toot.spoiler_text != "" %}
@ -73,7 +77,9 @@
{% endif %} {% endif %}
</div> </div>
<div class="action-bar"> <div class="action-bar">
{% if not toot.is_reblog %} {% if toot.is_myself %}
<span><i class="fa-solid fa-satellite-dish"></i><a href="{{ url_for('grab', toot_id=toot.id) }}"
target=" _blank">抓取回复</a></span>
<span><i class="fa-solid fa-up-right-and-down-left-from-center"></i><a <span><i class="fa-solid fa-up-right-and-down-left-from-center"></i><a
href="{{ url_for('context', toot_id=toot.id) }}" target=" _blank">上下文</a></span> href="{{ url_for('context', toot_id=toot.id) }}" target=" _blank">上下文</a></span>
{% endif %} {% endif %}

View file

@ -2,8 +2,9 @@
from mastodon import Mastodon from mastodon import Mastodon
from BDSM import db from BDSM import db
from BDSM.models import Reblog, Toot, Tag, Media, Emoji, Poll from BDSM.models import Other, Toot, Tag, Media, Emoji, Poll
import sys import sys
import dateutil.parser
def app_register(url): def app_register(url):
print("Registering app") print("Registering app")
@ -14,7 +15,7 @@ def app_register(url):
scopes=["read"] scopes=["read"]
) )
def archive_toot(url): def app_login(url):
mastodon = Mastodon( mastodon = Mastodon(
client_id='pyBDSM_clientcred.secret', client_id='pyBDSM_clientcred.secret',
access_token='user.secret', access_token='user.secret',
@ -35,21 +36,26 @@ def archive_toot(url):
# exit in either case # exit in either case
sys.exit(1) sys.exit(1)
return mastodon, user
def get_context(url, toot_id):
mastodon, user = app_login(url)
acct = mastodon.me().acct acct = mastodon.me().acct
statuses_count = str(mastodon.me().statuses_count) context = mastodon.status_context(toot_id)
statuses = []
statuses= context['ancestors'] + context['descendants']
toot_process(statuses, acct)
statuses = mastodon.account_statuses(user["id"], limit=20) db.session.commit()
# xx = statuses['created_at'].astimezone(tz_cn)
# pprint(xx.strftime("%m/%d/%Y, %H:%M:%S"))
# pprint(statuses)
happy_counter = 20
while(True):
def toot_process(statuses, my_acct, duplicates_counter=0):
for status in statuses: for status in statuses:
is_reblog = False is_reblog = False
is_myself = False
if status['reblog'] != None: if status['reblog'] != None:
if acct == status['reblog']['account']['acct']: if my_acct == status['reblog']['account']['acct']:
reblog_myself = True reblog_myself = True
else: else:
reblog_myself = False reblog_myself = False
@ -61,7 +67,7 @@ def archive_toot(url):
created_at = status['created_at'] created_at = status['created_at']
toot = Toot(id=id, created_at=created_at, reblog_myself=reblog_myself, reblog_id=reblog_id) toot = Toot(id=id, created_at=created_at, reblog_myself=reblog_myself, reblog_id=reblog_id)
db.session.add(toot) db.session.merge(toot)
# cur.execute('''INSERT OR REPLACE INTO TOOT (id,created_at,reblog_myself,reblog_id) \ # cur.execute('''INSERT OR REPLACE INTO TOOT (id,created_at,reblog_myself,reblog_id) \
# VALUES (?,?,?,?)''',(id, created_at, reblog_myself, reblog_id)) # VALUES (?,?,?,?)''',(id, created_at, reblog_myself, reblog_id))
@ -71,10 +77,23 @@ def archive_toot(url):
status = status['reblog'] status = status['reblog']
id = status['id'] id = status['id']
acct = status['account']['acct'] acct = status['account']['acct']
if my_acct == acct:
is_myself = True
else:
is_myself = False
url = status['url'] url = status['url']
created_at = status['created_at'] created_at = status['created_at']
edited_at = status['edited_at'] if status['edited_at'] != None else None
if 'edited_at' in status:
edited_at = status['edited_at']
if isinstance(edited_at, str):
edited_at = dateutil.parser.parse(status['edited_at'])
else:
edited_at = None
in_reply_to_id = status['in_reply_to_id'] in_reply_to_id = status['in_reply_to_id']
in_reply_to_account_id = status['in_reply_to_account_id'] in_reply_to_account_id = status['in_reply_to_account_id']
content = status['content'] content = status['content']
@ -86,7 +105,7 @@ def archive_toot(url):
media = Media(id=media_dict['id'], type=media_dict['type'], url=media_dict['url'], media = Media(id=media_dict['id'], type=media_dict['type'], url=media_dict['url'],
remote_url=media_dict['remote_url'], description=media_dict['description']) remote_url=media_dict['remote_url'], description=media_dict['description'])
db.session.add(media) db.session.merge(media)
# cur.execute('''INSERT OR REPLACE INTO MEDIA (id,type,url,remote_url,description) \ # cur.execute('''INSERT OR REPLACE INTO MEDIA (id,type,url,remote_url,description) \
# VALUES (?,?,?,?,?)''',(media_dict['id'], media_dict['type'], media_dict['url'], \ # VALUES (?,?,?,?,?)''',(media_dict['id'], media_dict['type'], media_dict['url'], \
# media_dict['remote_url'], media_dict['description'])) # media_dict['remote_url'], media_dict['description']))
@ -103,7 +122,7 @@ def archive_toot(url):
poll = Poll(id=poll_dict['id'], expires_at=expires_at, multiple=poll_dict['multiple'], \ poll = Poll(id=poll_dict['id'], expires_at=expires_at, multiple=poll_dict['multiple'], \
votes_count=poll_dict['votes_count'], options=options) votes_count=poll_dict['votes_count'], options=options)
db.session.add(poll) db.session.merge(poll)
# cur.execute('''INSERT OR REPLACE INTO POLL (id,expires_at,multiple,votes_count,options) \ # cur.execute('''INSERT OR REPLACE INTO POLL (id,expires_at,multiple,votes_count,options) \
# VALUES (?,?,?,?,?)''',(poll_dict['id'], expires_at, poll_dict['multiple'], \ # VALUES (?,?,?,?,?)''',(poll_dict['id'], expires_at, poll_dict['multiple'], \
# poll_dict['votes_count'], options)) # poll_dict['votes_count'], options))
@ -127,7 +146,7 @@ def archive_toot(url):
url=emoji['url'], url=emoji['url'],
static_url=emoji['static_url'], static_url=emoji['static_url'],
count=count) count=count)
db.session.add(emoji_data) db.session.merge(emoji_data)
# cur.execute('''INSERT INTO EMOJI (shortcode,url,static_url,count) \ # cur.execute('''INSERT INTO EMOJI (shortcode,url,static_url,count) \
# VALUES (?,?,?,?)''', (shortcode, emoji['url'], emoji['static_url'], count)) # VALUES (?,?,?,?)''', (shortcode, emoji['url'], emoji['static_url'], count))
else: else:
@ -146,7 +165,7 @@ def archive_toot(url):
if status['tags'] != []: if status['tags'] != []:
for tag in status['tags']: for tag in status['tags']:
tag_data = Tag(id=id, name=tag['name']) tag_data = Tag(id=id, name=tag['name'])
db.session.add(tag_data) db.session.merge(tag_data)
# cur.execute('''INSERT OR REPLACE INTO TAG (id,name) \ # cur.execute('''INSERT OR REPLACE INTO TAG (id,name) \
# VALUES (?,?)''',(id, tag['name'])) # VALUES (?,?)''',(id, tag['name']))
@ -160,7 +179,11 @@ def archive_toot(url):
favourites_count = status['favourites_count'] favourites_count = status['favourites_count']
language = status['language'] language = status['language']
table = Reblog() if is_reblog else Toot() if is_reblog or not is_myself:
table = Other()
else:
table = Toot()
table.id=id table.id=id
table.acct = acct table.acct = acct
table.url=url table.url=url
@ -183,18 +206,41 @@ def archive_toot(url):
table.favourites_count=favourites_count table.favourites_count=favourites_count
table.language=language table.language=language
db.session.add(table) if Toot.query.get(id) == None or Other.query.get(id) == None:
duplicates_counter += 1
db.session.merge(table)
# sql = f'''INSERT OR REPLACE INTO {table} (id,url,created_at,edited_at,in_reply_to_id,in_reply_to_account_id,content,\ # 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,\ # media_list,spoiler_text,poll_id,emoji_list,visibility,reblogged,favourited,bookmarked,sensitive,reblogs_count,\
# favourites_count,language) \ # favourites_count,language) \
# VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''' # VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)'''
# cur.execute(sql,(id,url,created_at,edited_at,in_reply_to_id,in_reply_to_account_id,content,media_list,spoiler_text,\ # 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)) # poll_id,emoji_list,visibility,reblogged,favourited,bookmarked,sensitive,reblogs_count,favourites_count,language))
return duplicates_counter
def archive_toot(url):
mastodon, user = app_login(url)
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
duplicates_counter = 0
while(True):
duplicates_counter = toot_process(statuses, acct)
db.session.commit() db.session.commit()
print(str(happy_counter) + ' / ' + statuses_count) print(str(happy_counter) + ' / ' + statuses_count)
happy_counter += 20 happy_counter += 20
if duplicates_counter >= 10:
print("检测到重复嘟文达到十次,取消存档……")
break
statuses = mastodon.fetch_next(statuses) statuses = mastodon.fetch_next(statuses)
# statuses = None # statuses = None
if statuses == None: if statuses == None:

View file

@ -5,8 +5,8 @@ import pytz
from flask import render_template, request, url_for, redirect, flash from flask import render_template, request, url_for, redirect, flash
from flask_sqlalchemy import Pagination from flask_sqlalchemy import Pagination
from BDSM import app, db from BDSM import app, db
from BDSM.models import Media, Settings, Toot, Emoji, Reblog from BDSM.models import Media, Settings, Toot, Emoji, Other
from BDSM.toot import app_register, archive_toot from BDSM.toot import app_register, archive_toot, get_context
from mastodon import Mastodon from mastodon import Mastodon
from types import SimpleNamespace from types import SimpleNamespace
from datetime import timezone from datetime import timezone
@ -53,7 +53,8 @@ def search():
def context(toot_id): def context(toot_id):
def get_reply(reply_id): def get_reply(reply_id):
toots = Toot.query.order_by(Toot.created_at.desc()).filter_by(in_reply_to_id=reply_id).all() toots = Toot.query.order_by(Toot.created_at.desc()).filter_by(in_reply_to_id=reply_id).all()
toots = process_toot(toots) other_toots = Other.query.order_by(Other.created_at.desc()).filter_by(in_reply_to_id=reply_id).all()
toots = process_toot(toots) + process_toot(other_toots)
for i in toots: for i in toots:
if i.in_reply_to_id != None: if i.in_reply_to_id != None:
@ -70,6 +71,8 @@ def context(toot_id):
while(in_reply_to_id != None): while(in_reply_to_id != None):
toot = [] toot = []
toot_ = Toot.query.get(toots[0].in_reply_to_id) toot_ = Toot.query.get(toots[0].in_reply_to_id)
if toot_ == None:
toot_ = Other.query.get(toots[0].in_reply_to_id)
if toot_ == None: if toot_ == None:
break break
@ -80,6 +83,17 @@ def context(toot_id):
return render_template('view.html', toots=toots,) return render_template('view.html', toots=toots,)
@app.route('/grab/<int:toot_id>', methods=['GET', 'POST'])
def grab(toot_id):
settings = Settings.query.first()
account = settings.account[1:]
username, domain = account.split("@")
url = "https://" + domain
get_context(url, toot_id)
flash('抓取完成……大概!')
return redirect(url_for('context',toot_id=toot_id))
@app.route('/settings', methods=['GET', 'POST']) @app.route('/settings', methods=['GET', 'POST'])
def settings(): def settings():
if request.method == 'POST': if request.method == 'POST':
@ -145,9 +159,6 @@ def archive():
settings = Settings.query.first() settings = Settings.query.first()
if settings == None: if settings == None:
return redirect(url_for('settings')) return redirect(url_for('settings'))
elif len(Toot.query.all()) > 0:
flash('现暂不支持重复存档!') #TODO
return redirect(url_for('index'))
else: else:
account = settings.account[1:] account = settings.account[1:]
username, domain = account.split("@") username, domain = account.split("@")
@ -173,15 +184,19 @@ def process_toot(toots_):
toot.created_at = toot.created_at.replace(tzinfo=timezone.utc) toot.created_at = toot.created_at.replace(tzinfo=timezone.utc)
toot.created_at = toot.created_at.astimezone(user_timezone).strftime(fmt) toot.created_at = toot.created_at.astimezone(user_timezone).strftime(fmt)
if hasattr(toot, 'reblog_id'):
toot.is_myself = True
if toot.reblog_id != None: if toot.reblog_id != None:
if toot.reblog_myself: if toot.reblog_myself:
toot = Toot.query.get(toot.reblog_id) toot = Toot.query.get(toot.reblog_id)
toot = SimpleNamespace(**toot.__dict__) toot = SimpleNamespace(**toot.__dict__)
toot.is_reblog = True toot.is_reblog = True
else: else:
toot = Reblog.query.get(toot.reblog_id) toot = Other.query.get(toot.reblog_id)
toot = SimpleNamespace(**toot.__dict__) toot = SimpleNamespace(**toot.__dict__)
toot.is_reblog = True toot.is_reblog = True
else:
toot.is_myself = False
if toot.media_list != "": if toot.media_list != "":
toot.medias = [] toot.medias = []