def edit_comment(request, comment_id): comment = get_object_or_404(Comment, id=comment_id) if not request.me.is_moderator: if comment.author != request.me: raise AccessDenied() if not comment.is_editable: raise AccessDenied( title="Этот комментарий больше нельзя редактировать") if not comment.post.is_visible or not comment.post.is_commentable: raise AccessDenied(title="Комментарии к этому посту были закрыты") post = comment.post if request.method == "POST": form = CommentForm(request.POST, instance=comment) if form.is_valid(): comment = form.save(commit=False) comment.is_deleted = False comment.html = None # flush cache comment.ipaddress = parse_ip_address(request) comment.useragent = parse_useragent(request) comment.save() return redirect("show_comment", post.slug, comment.id) else: form = CommentForm(instance=comment) return render(request, "comments/edit.html", { "comment": comment, "post": post, "form": form })
def do_user_admin_actions(request, user, data): if not request.me.is_moderator: raise AccessDenied() if user.is_god and not settings.DEBUG: raise AccessDenied(title="На этого юзера нельзя псить") # Hats if data["remove_hat"]: user.hat = None user.save() if data["add_hat"]: if data["new_hat"]: hat = HATS.get(data["new_hat"]) if hat: user.hat = {"code": data["new_hat"], **hat} user.save() else: user.hat = { "code": "custom", "title": data["new_hat_name"], "icon": data["new_hat_icon"], "color": data["new_hat_color"], } user.save() # Achievements if data["new_achievement"]: achievement = Achievement.objects.filter( code=data["new_achievement"]).first() if achievement: UserAchievement.objects.get_or_create( user=user, achievement=achievement, ) # Ban if data["is_banned"]: user.is_banned_until = datetime.utcnow() + timedelta( days=data["ban_days"]) user.save() # TODO: send email/bot with data["ban_reason"] # Unban if data["is_unbanned"]: user.is_banned_until = None user.save() # Unmoderate if data["is_rejected"]: user.moderation_status = User.MODERATION_STATUS_REJECTED user.save() return redirect("profile", user.slug)
def do_user_admin_actions(request, user, data): if not request.me.is_moderator: raise AccessDenied() if user.is_god and not settings.DEBUG: raise AccessDenied(title="На этого юзера нельзя псить") # Hats if data["remove_hat"]: user.hat = None user.save() if data["add_hat"]: if data["new_hat"]: hat = HATS.get(data["new_hat"]) if hat: user.hat = {"code": data["new_hat"], **hat} user.save() else: user.hat = { "code": "custom", "title": data["new_hat_name"], "icon": data["new_hat_icon"], "color": data["new_hat_color"], } user.save() # Achievements if data["add_achievement"]: achievement = ACHIEVEMENTS.get(data["new_achievement"]) if achievement: UserBadge.objects.get_or_create( user=user, type=data["new_achievement"], defaults=dict( name=achievement["title"], image=achievement["icon"], description=achievement.get("description"), style=achievement.get("style"), ), ) # Ban if data["is_banned"]: user.is_banned_until = datetime.utcnow() + timedelta( days=data["ban_days"] or 9999999) user.save() # TODO: send email/bot with data["ban_reason"] # Unmoderate if data["is_rejected"]: user.is_profile_rejected = True user.save() return redirect("profile", user.slug)
def godmode_invite(request): if not request.me.is_god: raise AccessDenied() if request.method == "POST": form = GodmodeInviteForm(request.POST, request.FILES) if form.is_valid(): email = form.cleaned_data["email"] days = form.cleaned_data["days"] now = datetime.utcnow() user, is_created = User.objects.get_or_create( email=email, defaults=dict( membership_platform_type=User.MEMBERSHIP_PLATFORM_DIRECT, full_name=email[:email.find("@")], membership_started_at=now, membership_expires_at=now + timedelta(days=days), created_at=now, updated_at=now, moderation_status=User.MODERATION_STATUS_INTRO, ), ) send_invited_email(request.me, user) return render( request, "message.html", { "title": "🎁 Юзер приглашен", "message": f"Сейчас он получит на почту '{email}' уведомление об этом. " f"Ему будет нужно залогиниться по этой почте и заполнить интро." }) else: form = GodmodeInviteForm() return render(request, "admin/simple_form.html", {"form": form})
def edit_post(request, post_slug): post = get_object_or_404(Post, slug=post_slug) if post.author != request.me and not request.me.is_moderator: raise AccessDenied() PostFormClass = POST_TYPE_MAP.get(post.type) or PostTextForm if request.method == "POST": form = PostFormClass(request.POST, request.FILES, instance=post) if form.is_valid(): post = form.save(commit=False) if not post.author: post.author = request.me post.html = None # flush cache post.save() SearchIndex.update_post_index(post) LinkedPost.create_links_from_text(post, post.text) if post.is_visible: return redirect("show_post", post.type, post.slug) else: return redirect("compose") else: form = PostFormClass(instance=post) return render(request, f"posts/compose/{post.type}.html", { "mode": "edit", "form": form })
def link_telegram(request): if not request.body: raise Http404() if request.method == "POST": data = json.loads(request.body) if not data.get("id") or not data.get("hash"): return render(request, "error.html", { "title": "Что-то пошло не так", "message": "Попробуйте авторизоваться снова.", }) if not is_valid_telegram_data(data, settings.TELEGRAM_TOKEN): raise AccessDenied(title="Подпись сообщения не совпадает") request.me.telegram_id = data["id"] request.me.telegram_data = data request.me.save() full_name = str(request.me.telegram_data.get("first_name") or "") \ + str(request.me.telegram_data.get("last_name") or "") return { "status": "success", "telegram": { "id": request.me.telegram_id, "username": request.me.telegram_data.get("username") or full_name, "full_name": full_name, } } return {"status": "nope"}
def debug_dev_login(request): if not settings.DEBUG: raise AccessDenied(title="Эта фича доступна только при DEBUG=true") user, is_created = User.objects.get_or_create( slug="dev", defaults=dict( patreon_id="123456", membership_platform_type=User.MEMBERSHIP_PLATFORM_PATREON, email="*****@*****.**", full_name="Senior 23 y.o. Developer", company="FAANG", position="Team Lead конечно", balance=10000, membership_started_at=datetime.utcnow(), membership_expires_at=datetime.utcnow() + timedelta(days=365 * 100), created_at=datetime.utcnow(), updated_at=datetime.utcnow(), is_email_verified=True, moderation_status=User.MODERATION_STATUS_APPROVED, roles=["god"], ), ) if is_created: Post.upsert_user_intro(user, "Очень плохое интро", is_visible=True) session = Session.create_for_user(user) return set_session_cookie(redirect("profile", user.slug), user, session)
def debug_random_login(request): if not settings.DEBUG: raise AccessDenied(title="Эта фича доступна только при DEBUG=true") slug = "random_" + random_string() user, is_created = User.objects.get_or_create( slug=slug, defaults=dict( patreon_id=random_string(), membership_platform_type=User.MEMBERSHIP_PLATFORM_PATREON, email=slug + "@random.dev", full_name="%s %d y.o. Developer" % (random.choice(["Максим", "Олег"]), random.randint(18, 101)), company="Acme Corp.", position=random.choice(["Подниматель пингвинов", "Опускатель серверов", "Коллектор пивных бутылок"]), balance=10000, membership_started_at=datetime.utcnow(), membership_expires_at=datetime.utcnow() + timedelta(days=365 * 100), created_at=datetime.utcnow(), updated_at=datetime.utcnow(), is_email_verified=True, moderation_status=User.MODERATION_STATUS_APPROVED, ), ) if is_created: Post.upsert_user_intro(user, "Интро как интро, аппрув прошло :Р", is_visible=True) session = Session.create_for_user(user) return set_session_cookie(redirect("profile", user.slug), user, session)
def do_post_curator_actions(request, post, data): if not request.me.is_curator: raise AccessDenied() do_common_admin_and_curator_actions(request, post, data) return redirect("show_post", post.type, post.slug)
def do_post_admin_actions(request, post, data): if not request.me.is_moderator: raise AccessDenied() do_common_admin_and_curator_actions(request, post, data) # Close comments if data["close_comments"]: post.is_commentable = False post.save() # Transfer ownership to the given username if data["transfer_ownership"]: user = User.objects.filter( slug=data["transfer_ownership"].strip()).first() if user: post.author = user post.save() if data["refresh_linked"]: LinkedPost.create_links_from_text(post, post.text) post_comments = Comment.visible_objects().filter(post=post, is_deleted=False) for comment in post_comments: LinkedPost.create_links_from_text(comment.post, comment.text) return redirect("show_post", post.type, post.slug)
def unpublish_post(request, post_slug): post = get_object_or_404(Post, slug=post_slug) if not request.me.is_moderator: if post.author != request.me: raise AccessDenied(title="Только автор или модератор может удалить пост") if not post.is_safely_deletable_by_author: raise AccessDenied( title="Только модератор может полностью удалить этот пост", message=f"Так как в нём уже больше {settings.MAX_COMMENTS_FOR_DELETE_VS_CLEAR} комментов " f"и некоторые из них могут быть ценны их авторам и коммьюнити в целом" ) post.unpublish() return redirect("show_post", post.type, post.slug)
def do_post_admin_actions(request, post, data): if not request.me.is_moderator: raise AccessDenied() # Change type if data["change_type"]: post.type = data["change_type"] post.save() # Labels if data["new_label"]: label = LABELS.get(data["new_label"]) if label: post.label = {"code": data["new_label"], **label} post.save() if data["remove_label"]: post.label = None post.save() # Pins if data["add_pin"]: post.is_pinned_until = datetime.utcnow() + timedelta( days=data["pin_days"]) post.save() if data["remove_pin"]: post.is_pinned_until = None post.save() # Moving up if data["move_up"]: post.last_activity_at = datetime.utcnow() post.save() # Shadow banning if data["shadow_ban"]: post.is_shadow_banned = True post.save() # Hide from main page if data["hide_on_main"]: post.is_visible_on_main_page = False post.save() # Close comments if data["close_comments"]: post.is_commentable = False post.save() # Transfer ownership to the given username if data["transfer_ownership"]: user = User.objects.filter( slug=data["transfer_ownership"].strip()).first() if user: post.author = user post.save() return redirect("show_post", post.type, post.slug)
def clear_post(request, post_slug): post = get_object_or_404(Post, slug=post_slug) if post.author != request.me and not request.me.is_moderator: raise AccessDenied(title="Только автор или модератор может очистить пост") post.clear() return redirect("show_post", post.type, post.slug)
def edit_comment(request, comment_id): comment = get_object_or_404(Comment, id=comment_id) if not request.me.is_moderator: if comment.author != request.me: raise AccessDenied() if comment.is_deleted: raise AccessDenied( title="Нельзя редактировать удаленный комментарий", message="Сначала тот, кто его удалил, должен его восстановить" ) if not comment.is_editable: hours = int(settings.COMMENT_EDITABLE_TIMEDELTA.total_seconds() // 3600) raise AccessDenied( title="Время вышло", message=f"Комментарий можно редактировать только в течение {hours} часов после создания" ) if not comment.post.is_visible or not comment.post.is_commentable: raise AccessDenied(title="Комментарии к этому посту закрыты") post = comment.post if request.method == "POST": form = CommentForm(request.POST, instance=comment) if form.is_valid(): comment = form.save(commit=False) comment.is_deleted = False comment.html = None # flush cache comment.ipaddress = parse_ip_address(request) comment.useragent = parse_useragent(request) comment.save() SearchIndex.update_comment_index(comment) return redirect("show_comment", post.slug, comment.id) else: form = CommentForm(instance=comment) return render(request, "comments/edit.html", { "comment": comment, "post": post, "form": form })
def debug_login(request, user_slug): if not (settings.DEBUG or settings.TESTS_RUN): raise AccessDenied(title="Эта фича доступна только при DEBUG=true") user = get_object_or_404(User, slug=user_slug) session = Session.create_for_user(user) return set_session_cookie(redirect("profile", user.slug), user, session)
def wrapper(request, *args, **kwargs): if not request.me: return redirect("login") if not request.me.is_moderator: raise AccessDenied() return view(request, *args, **kwargs)
def pin_comment(request, comment_id): comment = get_object_or_404(Comment, id=comment_id) if not request.me.is_moderator and comment.post.author != request.me: raise AccessDenied( title="Нельзя!", message="Только автор поста или модератор может пинить посты") if comment.reply_to: raise AccessDenied( title="Нельзя!", message="Можно пинить только комменты первого уровня") comment.is_pinned = not comment.is_pinned # toggle pin/unpin comment.save() return redirect("show_comment", comment.post.slug, comment.id)
def create_comment(request, post_slug): post = get_object_or_404(Post, slug=post_slug) if not post.is_commentable and not request.me.is_moderator: raise AccessDenied(title="Комментарии к этому посту закрыты") if request.POST.get("reply_to_id"): ProperCommentForm = ReplyForm elif post.type == Post.TYPE_BATTLE: ProperCommentForm = BattleCommentForm else: ProperCommentForm = CommentForm if request.method == "POST": form = ProperCommentForm(request.POST) if form.is_valid(): is_ok = Comment.check_rate_limits(request.me) if not is_ok: raise RateLimitException( title="🙅♂️ Вы комментируете слишком часто", message= "Подождите немного, вы достигли нашего лимита на комментарии в день. " "Можете написать нам в саппорт, пожаловаться об этом.") comment = form.save(commit=False) comment.post = post if not comment.author: comment.author = request.me comment.ipaddress = parse_ip_address(request) comment.useragent = parse_useragent(request) comment.save() # update the shitload of counters :) request.me.update_last_activity() Comment.update_post_counters(post) PostView.increment_unread_comments(comment) PostView.register_view( request=request, user=request.me, post=post, ) SearchIndex.update_comment_index(comment) LinkedPost.create_links_from_text(post, comment.text) return redirect("show_comment", post.slug, comment.id) else: log.error(f"Comment form error: {form.errors}") return render( request, "error.html", { "title": "Какая-то ошибка при публикации комментария 🤷♂️", "message": f"Мы уже получили оповещение и скоро пофиксим. " f"Ваш коммент мы сохранили чтобы вы могли скопировать его и запостить еще раз:", "data": form.cleaned_data.get("text") }) raise Http404()
def delete_comment(request, comment_id): comment = get_object_or_404(Comment, id=comment_id) if not request.me.is_moderator: # only comment author, post author or moderator can delete comments if comment.author != request.me and request.me != comment.post.author: raise AccessDenied( title="Нельзя!", message= "Только автор комментария, поста или модератор может удалить комментарий" ) if not comment.is_deletable: raise AccessDenied( title="Время вышло", message= "Комментарий можно удалить только в первые 3 дня после создания" ) if not comment.post.is_visible: raise AccessDenied( title="Пост скрыт!", message="Нельзя удалять комментарии к скрытому посту") if not comment.is_deleted: # delete comment comment.delete(deleted_by=request.me) PostView.decrement_unread_comments(comment) else: # undelete comment if comment.deleted_by == request.me or request.me.is_moderator: comment.undelete() PostView.increment_unread_comments(comment) else: raise AccessDenied( title="Нельзя!", message= "Только тот, кто удалил комментарий, может его восстановить") Comment.update_post_counters(comment.post) return redirect("show_comment", comment.post.slug, comment.id)
def edit_comment(request, comment_id): comment = get_object_or_404(Comment, id=comment_id) if not request.me.is_moderator: if comment.author != request.me: raise AccessDenied() if not comment.is_editable: raise AccessDenied( title="Время вышло", message= "Комментарий можно редактировать только в первые 3 часа после создания" ) if not comment.post.is_visible or not comment.post.is_commentable: raise AccessDenied(title="Комментарии к этому посту закрыты") post = comment.post if request.method == "POST": form = CommentForm(request.POST, instance=comment) if form.is_valid(): comment = form.save(commit=False) comment.is_deleted = False comment.html = None # flush cache comment.ipaddress = parse_ip_address(request) comment.useragent = parse_useragent(request) comment.save() SearchIndex.update_comment_index(comment) return redirect("show_comment", post.slug, comment.id) else: form = CommentForm(instance=comment) return render(request, "comments/edit.html", { "comment": comment, "post": post, "form": form })
def confirm_delete_account(request, user_slug): if request.method != "POST": return redirect("edit_account", user_slug, permanent=False) user = get_object_or_404(User, slug=user_slug) if user.id != request.me.id and not request.me.is_god: raise Http404() confirmation_hash = request.POST.get("secret_hash") code = request.POST.get("code") if confirmation_hash != user.secret_hash or not code: raise AccessDenied( title="Что-то не сходится", message= "Проверьте правильность кода и попробуйте запросить удаление аккаунта еще раз" ) # verify code (raises an exception) Code.check_code(recipient=user.email, code=code) # cancel payments cancel_all_stripe_subscriptions(user.stripe_id) # mark user for deletion user.deleted_at = datetime.utcnow() user.save() # remove sessions Session.objects.filter(user=user).delete() # schedule data cleanup task schedule("gdpr.forget.delete_user_data", user, next_run=datetime.utcnow() + settings.GDPR_DELETE_TIMEDELTA) # notify user async_task( send_delete_account_confirm_email, user=user, ) # notify admins async_task( send_telegram_message, chat=ADMIN_CHAT, text=f"💀 Юзер удалился: {settings.APP_HOST}/user/{user.slug}/", ) return render( request, "users/messages/delete_account_confirmed.html", )
def delete_post(request, post_slug): post = get_object_or_404(Post, slug=post_slug) if post.author != request.me: raise AccessDenied() if post.deleted_at: # restore post post.undelete() else: # delete post post.delete() return redirect("compose")
def god_settings(request): if not request.me.is_god: raise AccessDenied() god_settings = GodSettings.objects.first() if request.method == "POST": form = GodSettingsEditForm(request.POST, request.FILES, instance=god_settings) if form.is_valid(): form.save() else: form = GodSettingsEditForm(instance=god_settings) return render(request, "admin/godmode.html", {"form": form})
def debug_dev_login(request): if not settings.DEBUG: raise AccessDenied(title="Эта фича доступна только при DEBUG=true") user, is_created = User.objects.get_or_create( slug="dev", defaults=dict( membership_platform_type=User.MEMBERSHIP_PLATFORM_PATREON, membership_platform_id="DUMMY", email="*****@*****.**", full_name="Senior 23 y.o. Developer", company="FAANG", position="Team Lead конечно", balance=10000, membership_started_at=datetime.utcnow(), membership_expires_at=datetime.utcnow() + timedelta(days=365 * 100), created_at=datetime.utcnow(), updated_at=datetime.utcnow(), is_email_verified=True, is_profile_complete=True, is_profile_reviewed=True, is_profile_rejected=False, roles=["god"], ), ) if is_created: Post.upsert_user_intro(user, "Очень плохое интро", is_visible=True) session = Session.objects.create( user=user, token=random_string(length=32), created_at=datetime.utcnow(), expires_at=datetime.utcnow() + timedelta(days=365 * 100), ) response = redirect("profile", user.slug) response.set_cookie( key="token", value=session.token, max_age=settings.SESSION_COOKIE_AGE, httponly=True, secure=False, ) return response
def do_user_admin_actions(request, user, data): if not request.me.is_moderator: raise AccessDenied() # Hats if data["remove_hat"]: user.hat = None user.save() if data["add_hat"]: if data["new_hat"]: hat = HATS.get(data["new_hat"]) if hat: user.hat = {"code": data["new_hat"], **hat} user.save() else: user.hat = { "code": "custom", "title": data["new_hat_name"], "icon": data["new_hat_icon"], "color": data["new_hat_color"], } user.save() # Achievements if data["new_achievement"]: achievement = Achievement.objects.filter( code=data["new_achievement"]).first() if achievement: UserAchievement.objects.get_or_create( user=user, achievement=achievement, ) # Ban if data["is_banned"]: if not user.is_god: user.is_banned_until = datetime.utcnow() + timedelta( days=data["ban_days"]) user.save() if data["ban_days"] > 0: send_banned_email(user, days=data["ban_days"], reason=data["ban_reason"]) # Unban if data["is_unbanned"]: user.is_banned_until = None user.save() # Unmoderate if data["is_rejected"]: user.moderation_status = User.MODERATION_STATUS_REJECTED user.save() send_unmoderated_email(user) # Ping if data["ping"]: send_ping_email(user, message=data["ping"]) notify_user_ping(user, message=data["ping"]) notify_admin_user_ping(user, message=data["ping"]) return redirect("profile", user.slug)
def do_user_admin_actions(request, user, data): if not request.me.is_moderator: raise AccessDenied() # Hats if data["remove_hat"]: user.hat = None user.save() if data["add_hat"]: if data["new_hat"]: hat = HATS.get(data["new_hat"]) if hat: user.hat = {"code": data["new_hat"], **hat} user.save() else: user.hat = { "code": "custom", "title": data["new_hat_name"], "icon": data["new_hat_icon"], "color": data["new_hat_color"], } user.save() # Achievements if data["new_achievement"]: achievement = Achievement.objects.filter( code=data["new_achievement"]).first() if achievement: UserAchievement.objects.get_or_create( user=user, achievement=achievement, ) # Ban if data["is_banned"]: if not user.is_god: user.is_banned_until = datetime.utcnow() + timedelta( days=data["ban_days"]) user.save() if data["ban_days"] > 0: send_banned_email(user, days=data["ban_days"], reason=data["ban_reason"]) # Unban if data["is_unbanned"]: user.is_banned_until = None user.save() # Unmoderate if data["is_rejected"]: user.moderation_status = User.MODERATION_STATUS_REJECTED user.save() send_unmoderated_email(user) notify_admin_user_unmoderate(user) # Delete account if data["delete_account"] and request.me.is_god: user.membership_expires_at = datetime.utcnow() user.is_banned_until = datetime.utcnow() + timedelta(days=5000) # cancel recurring payments cancel_all_stripe_subscriptions(user.stripe_id) # mark user for deletion user.deleted_at = datetime.utcnow() user.save() # remove sessions Session.objects.filter(user=user).delete() # notify user send_delete_account_confirm_email(user=user, ) # notify admins send_telegram_message( chat=ADMIN_CHAT, text= f"💀 Юзер был удален админами: {settings.APP_HOST}/user/{user.slug}/", ) # Ping if data["ping"]: send_ping_email(user, message=data["ping"]) notify_user_ping(user, message=data["ping"]) notify_admin_user_ping(user, message=data["ping"]) return redirect("profile", user.slug)
def edit_post(request, post_slug): post = get_object_or_404(Post, slug=post_slug) if post.author != request.me and not request.me.is_moderator: raise AccessDenied() return create_or_edit(request, post.type, post=post, mode="edit")
def edit_post(request, post_slug): post = get_object_or_404(Post, slug=post_slug) if not post.can_edit(request.me): raise AccessDenied() return create_or_edit(request, post.type, post=post, mode="edit")
def godmode_settings(request): if not request.me.is_god: raise AccessDenied() return render(request, "admin/godmode.html")
def do_user_admin_actions(request, user, data): if not request.me.is_moderator: raise AccessDenied() # Roles if data["role"] and is_role_manageable_by_user(data["role"], request.me): role = data["role"] if data["role_action"] == "add" and role not in user.roles: user.roles.append(role) user.save() if data["role_action"] == "delete" and role in user.roles: user.roles.remove(role) user.save() # Hats if data["remove_hat"]: user.hat = None user.save() if data["add_hat"]: if data["new_hat"]: hat = HATS.get(data["new_hat"]) if hat: user.hat = {"code": data["new_hat"], **hat} user.save() else: user.hat = { "code": "custom", "title": data["new_hat_name"], "icon": data["new_hat_icon"], "color": data["new_hat_color"], } user.save() # Achievements if data["new_achievement"]: achievement = Achievement.objects.filter( code=data["new_achievement"]).first() if achievement: UserAchievement.objects.get_or_create( user=user, achievement=achievement, ) # Ban if data["is_banned"]: if not user.is_god: user.is_banned_until = datetime.utcnow() + timedelta( days=data["ban_days"]) user.save() if data["ban_days"] > 0: send_banned_email(user, days=data["ban_days"], reason=data["ban_reason"]) notify_admin_user_on_ban(user, days=data["ban_days"], reason=data["ban_reason"]) # Unmoderate if data["is_rejected"]: user.moderation_status = User.MODERATION_STATUS_REJECTED user.save() send_unmoderated_email(user) notify_admin_user_unmoderate(user) # Delete account if data["delete_account"] and request.me.is_god: user.membership_expires_at = datetime.utcnow() user.is_banned_until = datetime.utcnow() + timedelta(days=5000) # cancel recurring payments cancel_all_stripe_subscriptions(user.stripe_id) # mark user for deletion user.deleted_at = datetime.utcnow() user.save() # remove sessions Session.objects.filter(user=user).delete() # notify user send_delete_account_confirm_email(user=user, ) # notify admins send_telegram_message( chat=ADMIN_CHAT, text= f"💀 Юзер был удален админами: {settings.APP_HOST}/user/{user.slug}/", ) # Ping if data["ping"]: send_ping_email(user, message=data["ping"]) notify_user_ping(user, message=data["ping"]) notify_admin_user_ping(user, message=data["ping"]) # Add more days of membership if data["add_membership_days"] and int(data["add_membership_days"]) > 0: gift_membership_days( days=data["add_membership_days"], from_user=request.me, to_user=user, deduct_from_original_user=False, ) send_telegram_message( chat=ADMIN_CHAT, text= f"🎁 <b>Юзеру {user.slug} добавили {data['add_membership_days']} дней членства</b>", ) return redirect("profile", user.slug)