diff --git a/books/forms.py b/books/forms.py index 098fad5d..27abda07 100644 --- a/books/forms.py +++ b/books/forms.py @@ -1,17 +1,12 @@ from django import forms from django.utils.translation import gettext_lazy as _ -from .models import Book, BookMark, BookReview +from .models import Book, BookMark, BookReview, BookMarkStatusTranslation from common.models import MarkStatusEnum from common.forms import * def BookMarkStatusTranslator(status): - trans_dict = { - MarkStatusEnum.DO.value: _("在读"), - MarkStatusEnum.WISH.value: _("想读"), - MarkStatusEnum.COLLECT.value: _("读过") - } - return trans_dict[status] + return BookMarkStatusTranslation[status] class BookForm(forms.ModelForm): diff --git a/books/models.py b/books/models.py index 5e0c2ec5..6b14800b 100644 --- a/books/models.py +++ b/books/models.py @@ -2,12 +2,19 @@ import django.contrib.postgres.fields as postgres from django.utils.translation import gettext_lazy as _ from django.db import models from django.shortcuts import reverse -from common.models import Entity, Mark, Review, Tag +from common.models import Entity, Mark, Review, Tag, MarkStatusEnum from common.utils import GenerateDateUUIDMediaFilePath from django.conf import settings from django.db.models import Q +BookMarkStatusTranslation = { + MarkStatusEnum.DO.value: _("在读"), + MarkStatusEnum.WISH.value: _("想读"), + MarkStatusEnum.COLLECT.value: _("读过") +} + + def book_cover_path(instance, filename): return GenerateDateUUIDMediaFilePath(instance, filename, settings.BOOK_MEDIA_PATH_ROOT) @@ -115,7 +122,6 @@ class Book(Entity): def mark_class(self): return BookMark - class BookMark(Mark): book = models.ForeignKey( Book, on_delete=models.CASCADE, related_name='book_marks', null=True) @@ -126,6 +132,10 @@ class BookMark(Mark): fields=['owner', 'book'], name="unique_book_mark") ] + @property + def translated_status(self): + return BookMarkStatusTranslation[self.status] + class BookReview(Review): book = models.ForeignKey( @@ -137,6 +147,10 @@ class BookReview(Review): fields=['owner', 'book'], name="unique_book_review") ] + @property + def url(self): + return settings.APP_WEBSITE + reverse("books:retrieve_review", args=[self.id]) + class BookTag(Tag): book = models.ForeignKey( diff --git a/books/views.py b/books/views.py index e4ab00a1..68f4624f 100644 --- a/books/views.py +++ b/books/views.py @@ -10,8 +10,7 @@ from django.utils import timezone from django.core.paginator import Paginator from mastodon import mastodon_request_included from mastodon.models import MastodonApplication -from mastodon.api import post_toot, TootVisibilityEnum -from mastodon.utils import rating_to_emoji +from mastodon.api import share_mark, share_review from common.utils import PageLinksGenerator from common.views import PAGE_LINK_NUMBER, jump_or_scrape from common.models import SourceSiteEnum @@ -306,26 +305,7 @@ def create_update_mark(request): return HttpResponseServerError("integrity error") if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("books:retrieve", - args=[book.id]) - words = BookMarkStatusTranslator(form.cleaned_data['status']) +\ - f"《{book.title}》" + \ - rating_to_emoji(form.cleaned_data['rating'], MastodonApplication.objects.get(domain_name=request.user.mastodon_site).star_mode) - - # tags = settings.MASTODON_TAGS % {'category': '书', 'type': '标记'} - tags = '' - content = words + '\n' + url + '\n' + \ - form.cleaned_data['text'] + '\n' + tags - response = post_toot( - request.user.mastodon_site, content, visibility, request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error(f"CODE:{response.status_code} {response.text}") + if not share_mark(form.instance): return HttpResponseServerError("publishing mastodon status failed") else: return HttpResponseBadRequest(f"invalid form data {form.errors}") @@ -421,24 +401,7 @@ def create_review(request, book_id): form.instance.owner = request.user form.save() if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("books:retrieve_review", - args=[form.instance.id]) - words = "发布了关于" + f"《{form.instance.book.title}》" + "的评论" - # tags = settings.MASTODON_TAGS % {'category': '书', 'type': '评论'} - tags = '' - content = words + '\n' + url + \ - '\n' + form.cleaned_data['title'] + '\n' + tags - response = post_toot( - request.user.mastodon_site, content, visibility, request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_review(form.instance): return HttpResponseServerError("publishing mastodon status failed") return redirect(reverse("books:retrieve_review", args=[form.instance.id])) else: @@ -475,23 +438,7 @@ def update_review(request, id): form.instance.edited_time = timezone.now() form.save() if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("books:retrieve_review", - args=[form.instance.id]) - words = "发布了关于" + f"《{form.instance.book.title}》" + "的评论" - # tags = settings.MASTODON_TAGS % {'category': '书', 'type': '评论'} - tags = '' - content = words + '\n' + url + \ - '\n' + form.cleaned_data['title'] + '\n' + tags - response = post_toot( - request.user.mastodon_site, content, visibility, request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error(f"CODE:{response.status_code} {response.text}") + if not share_review(form.instance): return HttpResponseServerError("publishing mastodon status failed") return redirect(reverse("books:retrieve_review", args=[form.instance.id])) else: diff --git a/collection/views.py b/collection/views.py index 3be501d2..076a4dd3 100644 --- a/collection/views.py +++ b/collection/views.py @@ -11,7 +11,6 @@ from django.core.paginator import Paginator from mastodon import mastodon_request_included from mastodon.models import MastodonApplication from mastodon.api import post_toot, TootVisibilityEnum -from mastodon.utils import rating_to_emoji from common.utils import PageLinksGenerator from common.views import PAGE_LINK_NUMBER, jump_or_scrape from common.models import SourceSiteEnum diff --git a/common/models.py b/common/models.py index d11c420a..2cbec5b8 100644 --- a/common/models.py +++ b/common/models.py @@ -58,12 +58,16 @@ class Entity(models.Model): def get_absolute_url(self): raise NotImplementedError("Subclass should implement this method") + @property + def url(self): + return settings.APP_WEBSITE + self.get_absolute_url() + def get_json(self): return { 'title': self.title, 'brief': self.brief, 'rating': self.rating, - 'url': settings.APP_WEBSITE + self.get_absolute_url(), + 'url': self.url, 'cover_url': settings.APP_WEBSITE + self.cover.url, 'top_tags': self.tags[:5], 'category_name': self.verbose_category_name, @@ -273,6 +277,10 @@ class Mark(UserOwnedEntity): def __str__(self): return f"Mark({self.id} {self.owner} {self.status.upper()})" + @property + def translated_status(self): + raise NotImplementedError("Subclass should implement this.") + class Meta: abstract = True constraints = [ diff --git a/games/forms.py b/games/forms.py index 62789036..cda8b881 100644 --- a/games/forms.py +++ b/games/forms.py @@ -1,18 +1,13 @@ from django import forms from django.contrib.postgres.forms import SimpleArrayField from django.utils.translation import gettext_lazy as _ -from .models import Game, GameMark, GameReview +from .models import Game, GameMark, GameReview, GameMarkStatusTranslation from common.models import MarkStatusEnum from common.forms import * def GameMarkStatusTranslator(status): - trans_dict = { - MarkStatusEnum.DO.value: _("在玩"), - MarkStatusEnum.WISH.value: _("想玩"), - MarkStatusEnum.COLLECT.value: _("玩过") - } - return trans_dict[status] + return GameMarkStatusTranslation[status] class GameForm(forms.ModelForm): diff --git a/games/models.py b/games/models.py index 3238f51f..bb135c38 100644 --- a/games/models.py +++ b/games/models.py @@ -4,12 +4,19 @@ from django.utils.translation import gettext_lazy as _ from django.db import models from django.core.serializers.json import DjangoJSONEncoder from django.shortcuts import reverse -from common.models import Entity, Mark, Review, Tag +from common.models import Entity, Mark, Review, Tag, MarkStatusEnum from common.utils import ChoicesDictGenerator, GenerateDateUUIDMediaFilePath from django.utils import timezone from django.conf import settings +GameMarkStatusTranslation = { + MarkStatusEnum.DO.value: _("在玩"), + MarkStatusEnum.WISH.value: _("想玩"), + MarkStatusEnum.COLLECT.value: _("玩过") +} + + def game_cover_path(instance, filename): return GenerateDateUUIDMediaFilePath(instance, filename, settings.GAME_MEDIA_PATH_ROOT) @@ -112,6 +119,10 @@ class GameMark(Mark): fields=['owner', 'game'], name='unique_game_mark') ] + @property + def translated_status(self): + return GameMarkStatusTranslation[self.status] + class GameReview(Review): game = models.ForeignKey( @@ -123,6 +134,10 @@ class GameReview(Review): fields=['owner', 'game'], name='unique_game_review') ] + @property + def url(self): + return settings.APP_WEBSITE + reverse("games:retrieve_review", args=[self.id]) + class GameTag(Tag): game = models.ForeignKey( diff --git a/games/views.py b/games/views.py index eefb40aa..2882888b 100644 --- a/games/views.py +++ b/games/views.py @@ -10,8 +10,7 @@ from django.utils import timezone from django.core.paginator import Paginator from mastodon import mastodon_request_included from mastodon.models import MastodonApplication -from mastodon.api import post_toot, TootVisibilityEnum -from mastodon.utils import rating_to_emoji +from mastodon.api import share_mark, share_review from common.utils import PageLinksGenerator from common.views import PAGE_LINK_NUMBER, jump_or_scrape from common.models import SourceSiteEnum @@ -308,27 +307,7 @@ def create_update_mark(request): return HttpResponseServerError("integrity error") if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("games:retrieve", - args=[game.id]) - words = GameMarkStatusTranslator(form.cleaned_data['status']) +\ - f"《{game.title}》" + \ - rating_to_emoji(form.cleaned_data['rating'], MastodonApplication.objects.get(domain_name=request.user.mastodon_site).star_mode) - - # tags = settings.MASTODON_TAGS % {'category': '书', 'type': '标记'} - tags = '' - content = words + '\n' + url + '\n' + \ - form.cleaned_data['text'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_mark(form.instance): return HttpResponseServerError("publishing mastodon status failed") else: return HttpResponseBadRequest(f"invalid form data {form.errors}") @@ -424,24 +403,7 @@ def create_review(request, game_id): form.instance.owner = request.user form.save() if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("games:retrieve_review", - args=[form.instance.id]) - words = "发布了关于" + f"《{form.instance.game.title}》" + "的评论" - # tags = settings.MASTODON_TAGS % {'category': '书', 'type': '评论'} - tags = '' - content = words + '\n' + url + \ - '\n' + form.cleaned_data['title'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_review(form.instance): return HttpResponseServerError("publishing mastodon status failed") return redirect(reverse("games:retrieve_review", args=[form.instance.id])) else: @@ -478,24 +440,7 @@ def update_review(request, id): form.instance.edited_time = timezone.now() form.save() if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("games:retrieve_review", - args=[form.instance.id]) - words = "发布了关于" + f"《{form.instance.game.title}》" + "的评论" - # tags = settings.MASTODON_TAGS % {'category': '书', 'type': '评论'} - tags = '' - content = words + '\n' + url + \ - '\n' + form.cleaned_data['title'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_review(form.instance): return HttpResponseServerError("publishing mastodon status failed") return redirect(reverse("games:retrieve_review", args=[form.instance.id])) else: diff --git a/mastodon/api.py b/mastodon/api.py index c2152b80..1e51cecf 100644 --- a/mastodon/api.py +++ b/mastodon/api.py @@ -2,11 +2,16 @@ import requests import string import random import functools +import logging from django.core.exceptions import ObjectDoesNotExist from django.conf import settings from django.shortcuts import reverse from urllib.parse import quote from .models import CrossSiteUserInfo, MastodonApplication +from mastodon.utils import rating_to_emoji + + +logger = logging.getLogger(__name__) # See https://docs.joinmastodon.org/methods/accounts/ @@ -94,6 +99,8 @@ def post_toot(site, content, visibility, token, local_only=False): response = post(url, headers=headers, data=payload) if response.status_code == 201: response.status_code = 200 + if response.status_code != 200: + logger.error(f"Error {url} {response.status_code} {response.text}") return response @@ -187,7 +194,7 @@ def get_cross_site_id(target_user, target_site, token): cross_site_id = get_site_id( target_user.username, target_user.mastodon_site, target_site, token) if not cross_site_id: - print(f'unable to find cross_site_id for {target_user} on {target_site}') + logger.error(f'unable to find cross_site_id for {target_user} on {target_site}') return None cross_site_info = CrossSiteUserInfo.objects.create( uid=f"{target_user.username}@{target_user.mastodon_site}", @@ -210,9 +217,7 @@ def verify_account(site, token): try: response = get(url, headers={'User-Agent': 'NeoDB/1.0', 'Authorization': f'Bearer {token}'}) if response.status_code != 200: - print(url) - print(response.status_code) - print(response.text) + logger.error(f"Error {url} {response.status_code} {response.text}") return response.status_code, None r = response.json()['data'] r['display_name'] = r['name'] @@ -274,13 +279,13 @@ def get_mastodon_application(domain): # fill the form with returned data if response.status_code != 200: error_msg = "实例连接错误,代码: " + str(response.status_code) - print(f'Error connecting {domain}: {response.status_code} {response.content.decode("utf-8")}') + logger.error(f'Error connecting {domain}: {response.status_code} {response.content.decode("utf-8")}') else: try: data = response.json() except Exception as e: error_msg = "实例返回内容无法识别" - print(f'Error connecting {domain}: {response.status_code} {response.content.decode("utf-8")} {e}') + logger.error(f'Error connecting {domain}: {response.status_code} {response.content.decode("utf-8")} {e}') else: app = MastodonApplication.objects.create(domain_name=domain, app_id=data['id'], client_id=data['client_id'], client_secret=data['client_secret'], vapid_key=data['vapid_key'] if 'vapid_key' in data else '') @@ -320,9 +325,7 @@ def obtain_token(site, request, code): response = post(url, data=payload, headers=headers, auth=auth) # {"token_type":"bearer","expires_in":7200,"access_token":"VGpkOEZGR3FQRDJ5NkZ0dmYyYWIwS0dqeHpvTnk4eXp0NV9nWDJ2TEpmM1ZTOjE2NDg3ODMxNTU4Mzc6MToxOmF0OjE","scope":"block.read follows.read offline.access tweet.write users.read mute.read","refresh_token":"b1pXbGEzeUF1WE5yZHJOWmxTeWpvMTBrQmZPd0czLU0tQndZQTUyU3FwRDVIOjE2NDg3ODMxNTU4Mzg6MToxOnJ0OjE"} if response.status_code != 200: - print(url) - print(response.status_code) - print(response.text) + logger.error(f"Error {url} {response.status_code} {response.text}") return None, None data = response.json() return data.get('access_token'), data.get('refresh_token', '') @@ -342,10 +345,7 @@ def refresh_access_token(site, refresh_token): auth = (mast_app.client_id, mast_app.client_secret) response = post(url, data=payload, headers=headers, auth=auth) if response.status_code != 200: - print(url) - print(payload) - print(response.status_code) - print(response.text) + logger.error(f"Error {url} {response.status_code} {response.text}") return None data = response.json() return data.get('access_token') @@ -366,3 +366,35 @@ def revoke_token(site, token): url = 'https://' + site + API_REVOKE_TOKEN post(url, data=payload, headers={'User-Agent': 'NeoDB/1.0'}) + +def share_mark(mark): + user = mark.owner + if mark.visibility == 2: + visibility = TootVisibilityEnum.DIRECT + elif mark.visibility == 1: + visibility = TootVisibilityEnum.PRIVATE + elif user.preference.mastodon_publish_public: + visibility = TootVisibilityEnum.PUBLIC + else: + visibility = TootVisibilityEnum.UNLISTED + tags = '\n' + user.preference.mastodon_append_tag.replace('[category]', str(mark.item.verbose_category_name)) if user.preference.mastodon_append_tag else '' + stars = rating_to_emoji(mark.rating,MastodonApplication.objects.get(domain_name=user.mastodon_site).star_mode) + content = f"{mark.translated_status}《{mark.item.title}》{stars}\n{mark.item.url}\n{mark.text}{tags}" + response = post_toot(user.mastodon_site, content, visibility, user.mastodon_token) + return response.status_code == 200 + + +def share_review(review): + user = review.owner + if review.visibility == 2: + visibility = TootVisibilityEnum.DIRECT + elif review.visibility == 1: + visibility = TootVisibilityEnum.PRIVATE + elif user.preference.mastodon_publish_public: + visibility = TootVisibilityEnum.PUBLIC + else: + visibility = TootVisibilityEnum.UNLISTED + tags = '\n' + user.preference.mastodon_append_tag.replace('[category]', str(review.item.verbose_category_name)) if user.preference.mastodon_append_tag else '' + content = f"发布了关于《{review.item.title}》的评论\n{review.url}\n{review.title}{tags}" + response = post_toot(user.mastodon_site, content, visibility, user.mastodon_token) + return response.status_code == 200 diff --git a/movies/forms.py b/movies/forms.py index 2b4eff61..587d92e9 100644 --- a/movies/forms.py +++ b/movies/forms.py @@ -1,18 +1,13 @@ from django import forms from django.contrib.postgres.forms import SimpleArrayField from django.utils.translation import gettext_lazy as _ -from .models import Movie, MovieMark, MovieReview, MovieGenreEnum +from .models import Movie, MovieMark, MovieReview, MovieGenreEnum, MovieMarkStatusTranslation from common.models import MarkStatusEnum from common.forms import * def MovieMarkStatusTranslator(status): - trans_dict = { - MarkStatusEnum.DO.value: _("在看"), - MarkStatusEnum.WISH.value: _("想看"), - MarkStatusEnum.COLLECT.value: _("看过") - } - return trans_dict[status] + return MovieMarkStatusTranslation[status] class MovieForm(forms.ModelForm): diff --git a/movies/models.py b/movies/models.py index 2945d66a..c12544a2 100644 --- a/movies/models.py +++ b/movies/models.py @@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _ from django.db import models from django.core.serializers.json import DjangoJSONEncoder from django.shortcuts import reverse -from common.models import Entity, Mark, Review, Tag +from common.models import Entity, Mark, Review, Tag, MarkStatusEnum from common.utils import ChoicesDictGenerator, GenerateDateUUIDMediaFilePath from django.utils import timezone from django.conf import settings @@ -12,6 +12,13 @@ from django.db.models import Q import re +MovieMarkStatusTranslation = { + MarkStatusEnum.DO.value: _("在看"), + MarkStatusEnum.WISH.value: _("想看"), + MarkStatusEnum.COLLECT.value: _("看过") +} + + def movie_cover_path(instance, filename): return GenerateDateUUIDMediaFilePath(instance, filename, settings.MOVIE_MEDIA_PATH_ROOT) @@ -231,11 +238,16 @@ class Movie(Entity): class MovieMark(Mark): movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='movie_marks', null=True) + class Meta: constraints = [ models.UniqueConstraint(fields=['owner', 'movie'], name='unique_movie_mark') ] + @property + def translated_status(self): + return MovieMarkStatusTranslation[self.status] + class MovieReview(Review): movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='movie_reviews', null=True) @@ -245,6 +257,10 @@ class MovieReview(Review): fields=['owner', 'movie'], name='unique_movie_review') ] + @property + def url(self): + return settings.APP_WEBSITE + reverse("movies:retrieve_review", args=[self.id]) + class MovieTag(Tag): movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='movie_tags', null=True) diff --git a/movies/views.py b/movies/views.py index 5a4c4c25..57e02fca 100644 --- a/movies/views.py +++ b/movies/views.py @@ -10,8 +10,7 @@ from django.utils import timezone from django.core.paginator import Paginator from mastodon import mastodon_request_included from mastodon.models import MastodonApplication -from mastodon.api import post_toot, TootVisibilityEnum -from mastodon.utils import rating_to_emoji +from mastodon.api import share_mark, share_review from common.utils import PageLinksGenerator from common.views import PAGE_LINK_NUMBER, jump_or_scrape from common.models import SourceSiteEnum @@ -307,27 +306,7 @@ def create_update_mark(request): return HttpResponseServerError("integrity error") if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("movies:retrieve", - args=[movie.id]) - words = MovieMarkStatusTranslator(form.cleaned_data['status']) +\ - f"《{movie.title}》" + \ - rating_to_emoji(form.cleaned_data['rating'], MastodonApplication.objects.get(domain_name=request.user.mastodon_site).star_mode) - - # tags = settings.MASTODON_TAGS % {'category': '书', 'type': '标记'} - tags = '' - content = words + '\n' + url + '\n' + \ - form.cleaned_data['text'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_mark(form.instance): return HttpResponseServerError("publishing mastodon status failed") else: return HttpResponseBadRequest(f"invalid form data {form.errors}") @@ -423,24 +402,7 @@ def create_review(request, movie_id): form.instance.owner = request.user form.save() if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("movies:retrieve_review", - args=[form.instance.id]) - words = "发布了关于" + f"《{form.instance.movie.title}》" + "的评论" - # tags = settings.MASTODON_TAGS % {'category': '书', 'type': '评论'} - tags = '' - content = words + '\n' + url + \ - '\n' + form.cleaned_data['title'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_review(form.instance): return HttpResponseServerError("publishing mastodon status failed") return redirect(reverse("movies:retrieve_review", args=[form.instance.id])) else: @@ -477,24 +439,7 @@ def update_review(request, id): form.instance.edited_time = timezone.now() form.save() if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("movies:retrieve_review", - args=[form.instance.id]) - words = "发布了关于" + f"《{form.instance.movie.title}》" + "的评论" - # tags = settings.MASTODON_TAGS % {'category': '书', 'type': '评论'} - tags = '' - content = words + '\n' + url + \ - '\n' + form.cleaned_data['title'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_review(form.instance): return HttpResponseServerError("publishing mastodon status failed") return redirect(reverse("movies:retrieve_review", args=[form.instance.id])) else: diff --git a/music/forms.py b/music/forms.py index 6cfb54e8..9e487592 100644 --- a/music/forms.py +++ b/music/forms.py @@ -7,12 +7,7 @@ from common.forms import * def MusicMarkStatusTranslator(status): - trans_dict = { - MarkStatusEnum.DO.value: _("在听"), - MarkStatusEnum.WISH.value: _("想听"), - MarkStatusEnum.COLLECT.value: _("听过") - } - return trans_dict[status] + return MusicMarkStatusTranslation[status] class SongForm(forms.ModelForm): diff --git a/music/models.py b/music/models.py index f66743b8..b75811c0 100644 --- a/music/models.py +++ b/music/models.py @@ -4,12 +4,19 @@ from django.utils.translation import gettext_lazy as _ from django.db import models from django.core.serializers.json import DjangoJSONEncoder from django.shortcuts import reverse -from common.models import Entity, Mark, Review, Tag, SourceSiteEnum +from common.models import Entity, Mark, Review, Tag, SourceSiteEnum, MarkStatusEnum from common.utils import ChoicesDictGenerator, GenerateDateUUIDMediaFilePath from django.utils import timezone from django.conf import settings +MusicMarkStatusTranslation = { + MarkStatusEnum.DO.value: _("在听"), + MarkStatusEnum.WISH.value: _("想听"), + MarkStatusEnum.COLLECT.value: _("听过") +} + + def song_cover_path(instance, filename): return GenerateDateUUIDMediaFilePath(instance, filename, settings.SONG_MEDIA_PATH_ROOT) @@ -146,6 +153,10 @@ class SongMark(Mark): fields=['owner', 'song'], name='unique_song_mark') ] + @property + def translated_status(self): + return MusicMarkStatusTranslation[self.status] + class SongReview(Review): song = models.ForeignKey( @@ -157,6 +168,10 @@ class SongReview(Review): fields=['owner', 'song'], name='unique_song_review') ] + @property + def url(self): + return settings.APP_WEBSITE + reverse("songs:retrieve_review", args=[self.id]) + class SongTag(Tag): song = models.ForeignKey( @@ -181,6 +196,10 @@ class AlbumMark(Mark): fields=['owner', 'album'], name='unique_album_mark') ] + @property + def translated_status(self): + return MusicMarkStatusTranslation[self.status] + class AlbumReview(Review): album = models.ForeignKey( @@ -192,6 +211,10 @@ class AlbumReview(Review): fields=['owner', 'album'], name='unique_album_review') ] + @property + def url(self): + return settings.APP_WEBSITE + reverse("albums:retrieve_review", args=[self.id]) + class AlbumTag(Tag): album = models.ForeignKey( diff --git a/music/views.py b/music/views.py index 7429f203..c9911c7b 100644 --- a/music/views.py +++ b/music/views.py @@ -4,8 +4,7 @@ from common.models import SourceSiteEnum from common.views import PAGE_LINK_NUMBER, jump_or_scrape from common.utils import PageLinksGenerator from mastodon.models import MastodonApplication -from mastodon.utils import rating_to_emoji -from mastodon.api import post_toot, TootVisibilityEnum +from mastodon.api import share_mark, share_review from mastodon import mastodon_request_included from django.core.paginator import Paginator from django.utils import timezone @@ -326,27 +325,7 @@ def create_update_song_mark(request): return HttpResponseServerError("integrity error") if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("music:retrieve_song", - args=[song.id]) - words = MusicMarkStatusTranslator(form.cleaned_data['status']) +\ - f"《{song.title}》" + \ - rating_to_emoji(form.cleaned_data['rating'], MastodonApplication.objects.get(domain_name=request.user.mastodon_site).star_mode) - - # tags = MASTODON_TAGS % {'category': '书', 'type': '标记'} - tags = '' - content = words + '\n' + url + '\n' + \ - form.cleaned_data['text'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_review(form.instance): return HttpResponseServerError("publishing mastodon status failed") else: return HttpResponseBadRequest(f"invalid form data {form.errors}") @@ -442,24 +421,7 @@ def create_song_review(request, song_id): form.instance.owner = request.user form.save() if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("music:retrieve_song_review", - args=[form.instance.id]) - words = "发布了关于" + f"《{form.instance.song.title}》" + "的评论" - # tags = MASTODON_TAGS % {'category': '书', 'type': '评论'} - tags = '' - content = words + '\n' + url + \ - '\n' + form.cleaned_data['title'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_review(form.instance): return HttpResponseServerError("publishing mastodon status failed") return redirect(reverse("music:retrieve_song_review", args=[form.instance.id])) else: @@ -496,24 +458,7 @@ def update_song_review(request, id): form.instance.edited_time = timezone.now() form.save() if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("music:retrieve_song_review", - args=[form.instance.id]) - words = "发布了关于" + f"《{form.instance.song.title}》" + "的评论" - # tags = MASTODON_TAGS % {'category': '书', 'type': '评论'} - tags = '' - content = words + '\n' + url + \ - '\n' + form.cleaned_data['title'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_review(form.instance): return HttpResponseServerError("publishing mastodon status failed") return redirect(reverse("music:retrieve_song_review", args=[form.instance.id])) else: @@ -921,27 +866,7 @@ def create_update_album_mark(request): return HttpResponseServerError("integrity error") if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("music:retrieve_album", - args=[album.id]) - words = MusicMarkStatusTranslator(form.cleaned_data['status']) +\ - f"《{album.title}》" + \ - rating_to_emoji(form.cleaned_data['rating'], MastodonApplication.objects.get(domain_name=request.user.mastodon_site).star_mode) - - # tags = MASTODON_TAGS % {'category': '书', 'type': '标记'} - tags = '' - content = words + '\n' + url + '\n' + \ - form.cleaned_data['text'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_mark(form.instance): return HttpResponseServerError("publishing mastodon status failed") else: return HttpResponseBadRequest(f"invalid form data {form.errors}") @@ -1037,24 +962,7 @@ def create_album_review(request, album_id): form.instance.owner = request.user form.save() if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("music:retrieve_album_review", - args=[form.instance.id]) - words = "发布了关于" + f"《{form.instance.album.title}》" + "的评论" - # tags = MASTODON_TAGS % {'category': '书', 'type': '评论'} - tags = '' - content = words + '\n' + url + \ - '\n' + form.cleaned_data['title'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_review(form.instance): return HttpResponseServerError("publishing mastodon status failed") return redirect(reverse("music:retrieve_album_review", args=[form.instance.id])) else: @@ -1091,24 +999,7 @@ def update_album_review(request, id): form.instance.edited_time = timezone.now() form.save() if form.cleaned_data['share_to_mastodon']: - if form.cleaned_data['visibility'] == 2: - visibility = TootVisibilityEnum.DIRECT - elif form.cleaned_data['visibility'] == 1: - visibility = TootVisibilityEnum.PRIVATE - else: - visibility = TootVisibilityEnum.PUBLIC if request.user.preference.mastodon_publish_public else TootVisibilityEnum.UNLISTED - url = "https://" + request.get_host() + reverse("music:retrieve_album_review", - args=[form.instance.id]) - words = "发布了关于" + f"《{form.instance.album.title}》" + "的评论" - # tags = MASTODON_TAGS % {'category': '书', 'type': '评论'} - tags = '' - content = words + '\n' + url + \ - '\n' + form.cleaned_data['title'] + '\n' + tags - response = post_toot(request.user.mastodon_site, content, visibility, - request.user.mastodon_token) - if response.status_code != 200: - mastodon_logger.error( - f"CODE:{response.status_code} {response.text}") + if not share_review(form.instance): return HttpResponseServerError("publishing mastodon status failed") return redirect(reverse("music:retrieve_album_review", args=[form.instance.id])) else: diff --git a/users/models.py b/users/models.py index 752c4ad5..c118724d 100644 --- a/users/models.py +++ b/users/models.py @@ -115,6 +115,7 @@ class Preference(models.Model): ) export_status = models.JSONField(blank=True, null=True, encoder=DjangoJSONEncoder, default=dict) mastodon_publish_public = models.BooleanField(null=False, default=False) + mastodon_append_tag = models.CharField(max_length=2048, default='') def get_serialized_home_layout(self): return str(self.home_layout).replace("\'", "\"") diff --git a/users/templates/users/preferences.html b/users/templates/users/preferences.html index 50d7f5c7..1a37bbbc 100644 --- a/users/templates/users/preferences.html +++ b/users/templates/users/preferences.html @@ -29,21 +29,24 @@
-
{% trans '联邦网络(Mastodon/Pleroma)相关设置' %}
+
{% trans '社交网络分享相关设置' %}
-
+ {% csrf_token %} - {% trans '以公开方式分享的帖文是否发布到公共时间轴上:' %} + {% trans '在联邦网络上以公开方式分享的帖文是否发布到公共时间轴上:' %}
- +
+

+ {% trans '分享贴文时附加标签:' %} +
+ + +
+
- +
diff --git a/users/views.py b/users/views.py index 146781ce..74ed17f3 100644 --- a/users/views.py +++ b/users/views.py @@ -1048,8 +1048,9 @@ def auth_logout(request): def preferences(request): if request.method == 'POST': request.user.preference.mastodon_publish_public = bool(request.POST.get('mastodon_publish_public')) + request.user.preference.mastodon_append_tag = request.POST.get('mastodon_append_tag', '').strip() request.user.preference.save() - return render(request, 'users/preferences.html', {'mastodon_publish_public': request.user.preference.mastodon_publish_public}) + return render(request, 'users/preferences.html') @mastodon_request_included