示例#1
0
def mod_take_pid(pid, v):

    bid = request.form.get("board_id",None)
    if not bid:
        abort(400)

    board=get_board(bid)

    if board.is_banned:
        abort(403)

    if not board.has_mod(v):
        abort(403)

    post = get_post(pid)

    if not post.board_id==1:
        abort(422)

    if board.has_ban(post.author):
        abort(403)

    if not board.can_take(post):
        abort(403)

    post.board_id=board.id
    post.guild_name=board.name
    db.add(post)
    db.commit()

    #clear board's listing caches
    cache.delete_memoized(Board.idlist, board)
    
    return redirect(post.permalink)
示例#2
0
def mod_toggle_post_pin(bid, pid, x, board, v):

    post=get_post(pid)

    if post.board_id != board.id:
        abort(422)

    try:
        x=bool(int(x))
    except:
        abort(422)

    if x and not board.can_pin_another:
        return jsonify({"error":f"+{board.name} already has the maximum number of pinned posts."}), 409


    post.is_pinned=x


    cache.delete_memoized(Board.idlist, post.board)

    db.add(post)
    db.commit()

    return "", 204
示例#3
0
def user_kick_pid(pid, v):

    #allows a user to yank their content back to +general if it was there previously
    
    post=get_post(pid)

    current_board=post.board

    if not post.author_id==v.id:
        abort(403)

    if post.board_id==post.original_board_id:
        abort(403)

    if post.board_id==1:
        abort(400)

    #block further yanks to the same board
    new_rel=PostRelationship(post_id=post.id,
                             board_id=post.board.id)
    db.add(new_rel)

    post.board_id=1
    post.guild_name="general"
    post.is_pinned=False
    
    db.add(post)
    db.commit()
    
    #clear board's listing caches
    cache.delete_memoized(Board.idlist, current_board)
    
    return "", 204
示例#4
0
def ban_post(post_id, v):

    post = g.db.query(Submission).filter_by(id=base36decode(post_id)).first()

    if not post:
        abort(400)

    post.is_banned = True
    post.is_approved = 0
    post.approved_utc = 0
    post.stickied = False
    post.is_pinned = False

    ban_reason=request.form.get("reason", "")
    with CustomRenderer() as renderer:
        ban_reason = renderer.render(mistletoe.Document(ban_reason))
    ban_reason = sanitize(ban_reason, linkgen=True)

    post.ban_reason = ban_reason

    g.db.add(post)

    cache.delete_memoized(Board.idlist, post.board)

    ma=ModAction(
        kind="ban_post",
        user_id=v.id,
        target_submission_id=post.id,
        board_id=post.board_id,
        note="admin action"
        )
    g.db.add(ma)
    return (redirect(post.permalink), post)
示例#5
0
def settings_block_user(v):

    user = get_user(request.values.get("username"), graceful=True)

    if not user:
        return jsonify({"error": "That user doesn't exist."}), 404

    if user.id == v.id:
        return jsonify({"error": "You can't block yourself."}), 409

    if v.has_block(user):
        return jsonify(
            {"error": f"You have already blocked @{user.username}."}), 409

    if user.id == 1:
        return jsonify({"error": "You can't block @ruqqus."}), 409

    new_block = UserBlock(user_id=v.id,
                          target_id=user.id,
                          created_utc=int(time.time()))
    g.db.add(new_block)

    cache.delete_memoized(v.idlist)
    #cache.delete_memoized(Board.idlist, v=v)
    cache.delete_memoized(frontlist, v=v)

    return jsonify({"message": f"@{user.username} blocked."})
示例#6
0
def subscribe_board(boardname, v):

    board = get_guild(boardname)

    #check for existing subscription, canceled or otherwise
    sub = g.db.query(Subscription).filter_by(user_id=v.id,
                                             board_id=board.id).first()
    if sub:
        if sub.is_active:
            return jsonify(
                {"error": f"You are already a member of +{board.name}"}), 409
        else:
            #reactivate canceled sub
            sub.is_active = True
            g.db.add(sub)

            return jsonify({"message": f"Joined +{board.name}"}), 200

    new_sub = Subscription(user_id=v.id, board_id=board.id)

    g.db.add(new_sub)
    g.db.flush()

    #clear your cached guild listings
    cache.delete_memoized(User.idlist, v, kind="board")

    #update board trending rank
    board.rank_trending = board.trending_rank
    board.stored_subscriber_count = board.subscriber_count
    g.db.add(board)

    return jsonify({"message": f"Joined +{board.name}"}), 200
示例#7
0
def mod_board_color(bid, board, v):

    color = str(request.form.get("color", ""))

    if len(color) != 6:
        color = "603abb"

    red = color[0:1]
    green = color[2:3]
    blue = color[4:5]

    try:
        if any([int(x, 16) > 255 for x in [red, green, blue]]):
            color = "603abb"
    except ValueError:
        color = "603abb"

    board.color = color
    board.color_nonce += 1

    g.db.add(board)

    try:
        cache.delete_memoized(board_css, board.name)
        cache.delete_memoized(board_dark_css, board.name)
    except:
        pass

    return redirect(f"/+{board.name}/mod/appearance?msg=Success")
示例#8
0
def subscribe_board(boardname, v):

    board=get_guild(boardname)

    #check for existing subscription, canceled or otherwise
    sub= g.db.query(Subscription).filter_by(user_id=v.id, board_id=board.id).first()
    if sub:
        if sub.is_active:
            abort(409)
        else:
            #reactivate canceled sub
            sub.is_active=True
            g.db.add(sub)
            
            return "", 204

    
    new_sub=Subscription(user_id=v.id,
                         board_id=board.id)

    g.db.add(new_sub)
    

    #clear your cached guild listings
    cache.delete_memoized(User.idlist, v, kind="board")

    return "", 204
示例#9
0
def mod_take_pid(pid, v):

    bid = request.form.get("board_id",None)
    if not bid:
        abort(400)

    board=get_board(bid)
    post = get_post(pid)

    if board.is_banned:
        return jsonify({'error':f"+{board.name} is banned. You can't yank anything there."}), 403

    if not post.board_id==1:
        return jsonify({'error':f"This post is no longer in +general"}), 403

    if not board.has_mod(v):
        return jsonify({'error':f"You are no longer a guildmaster of +{board.name}"}), 403

    if board.has_ban(post.author):
        return jsonify({'error':f"@{post.author.username} is exiled from +{board.name}, so you can't yank their post there."}), 403

    if not board.can_take(post):
        return jsonify({'error':f"You can't yank this particular post to +{board.name}."}), 403

    post.board_id=board.id
    post.guild_name=board.name
    g.db.add(post)
    

    #clear board's listing caches
    cache.delete_memoized(Board.idlist, board)
    
    return "", 204
示例#10
0
def unsubscribe_board(boardname, v):

    board = get_guild(boardname)

    #check for existing subscription
    sub = g.db.query(Subscription).filter_by(user_id=v.id,
                                             board_id=board.id).first()

    if not sub:
        abort(409)
    elif not sub.is_active:
        abort(409)

    sub.is_active = False

    g.db.add(sub)
    g.db.flush()

    #clear your cached guild listings
    cache.delete_memoized(User.idlist, v, kind="board")

    board.rank_trending = board.trending_rank
    g.db.add(board)

    return "", 204
示例#11
0
def mod_board_color(bid, board, v):

    color=str(request.form.get("color","")).strip()

    #Remove the '#' from the beginning in case it was entered.
    if color.startswith('#'):
        color = color[1:]

    if len(color) !=6:
        return render_template("guild/appearance.html", v=v, b=board, error="Invalid color code."), 400

    red=color[0:1]
    green=color[2:3]
    blue=color[4:5]

    try:
        if any([int(x,16)>255 for x in [red,green,blue]]):
            return render_template("guild/appearance.html", v=v, b=board, error="Invalid color code."), 400
    except ValueError:
        return render_template("guild/appearance.html", v=v, b=board, error="Invalid color code."), 400

    board.color=color
    board.color_nonce+=1
    
    g.db.add(board)
    

    try:
        cache.delete_memoized(board_css, board.name)
        cache.delete_memoized(board_dark_css, board.name)
    except:
        pass
    
    return redirect(f"/+{board.name}/mod/appearance?msg=Success")
示例#12
0
def mod_take_pid(pid, v):

    bid = request.form.get("board_id", request.form.get("guild", None))
    if not bid:
        abort(400)

    board = get_board(bid)
    post = get_post(pid)

    #check cooldowns
    now=int(time.time())
    if post.original_board_id != board.id and post.author_id != v.id:
        if now <  v.last_yank_utc + 3600:
            return jsonify({'error':f"You've yanked a post recently. You need to wait 1 hour between yanks."}), 401
        elif now <  board.last_yank_utc + 3600:
            return jsonify({'error':f"+{board.name} has yanked a post recently. The Guild needs to wait 1 hour between yanks."}), 401


    if board.is_banned:
        return jsonify({'error': f"+{board.name} is banned. You can't yank anything there."}), 403

    if not post.board_id == 1:
        return jsonify({'error': f"This post is no longer in +general"}), 403

    if not board.has_mod(v):
        return jsonify({'error': f"You are no longer a guildmaster of +{board.name}"}), 403

    if board.has_ban(post.author):
        return jsonify({'error': f"@{post.author.username} is exiled from +{board.name}, so you can't yank their post there."}), 403

    if post.author.any_block_exists(v):
        return jsonify({'error': f"You can't yank @{post.author.username}'s content."}), 403

    if not board.can_take(post):
        return jsonify({'error': f"You can't yank this particular post to +{board.name}."}), 403

    if board.is_private and post.original_board_id != board.id:
        return jsonify({'error': f"+{board.name} is private, so you can only yank content that started there."}), 403

    post.board_id = board.id
    post.guild_name = board.name
    g.db.add(post)

    if post.original_board_id != board.id and post.author_id != v.id:
        board.last_yank_utc=now
        v.last_yank_utc=now

        g.db.add(board)
        g.db.add(v)

        notif_text=f"Your post [{post.title}]({post.permalink}) has been Yanked from +general to +{board.name}.\n\nIf you don't want it there, just click `Remove from +{board.name}` on the post."
        send_notification(post.author, notif_text)
        g.db.commit()

    # clear board's listing caches
    cache.delete_memoized(Board.idlist, board)

    return "", 204
示例#13
0
def delete_post_pid(pid, v):

    post = get_post(pid)
    if not post.author_id == v.id:
        abort(403)

    post.is_deleted = True

    db.add(post)

    #clear cache
    cache.delete_memoized(User.userpagelisting, v, sort="new")
    cache.delete_memoized(Board.idlist, post.board)

    if post.age >= 3600 * 6:
        cache.delete_memoized(Board.idlist, post.board, sort="new")
        cache.delete_memoized(frontlist, sort="new")

    #delete i.ruqqus.com
    if post.domain == "i.ruqqus.com":

        segments = post.url.split("/")
        pid = segments[4]
        rand = segments[5]
        if pid == post.base36id:
            key = f"post/{pid}/{rand}"
            delete_file(key)
            post.is_image = False
            db.add(post)

    db.commit()

    return "", 204
示例#14
0
def settings_unblock_guild(v):

    board = get_guild(request.values.get("board"), graceful=True)

    x = v.has_blocked_guild(board)
    if not x:
        abort(409)

    g.db.delete(x)

    cache.delete_memoized(v.idlist)
    #cache.delete_memoized(Board.idlist, v=v)
    cache.delete_memoized(frontlist, v=v)

    return jsonify({"message": f"+{board.name} removed from filter"})
示例#15
0
def mod_kick_bid_pid(bid, pid, board, v):

    post = get_post(pid)

    if not post.board_id == board.id:
        abort(400)

    post.board_id = 1
    post.guild_name = "general"
    post.is_pinned = False
    g.db.add(post)

    cache.delete_memoized(Board.idlist, board)

    return "", 204
示例#16
0
def settings_unblock_user(v):

    user = get_user(request.values.get("username"))

    x = v.has_block(user)
    if not x:
        abort(409)

    g.db.delete(x)

    cache.delete_memoized(v.idlist)
    #cache.delete_memoized(Board.idlist, v=v)
    cache.delete_memoized(frontlist, v=v)

    return jsonify({"message": f"@{user.username} unblocked."})
示例#17
0
def settings_unblock_guild(v):

    board = get_guild(request.values.get("board"), graceful=True)

    x = v.has_blocked_guild(board)
    if not x:
        abort(409)

    g.db.delete(x)

    cache.delete_memoized(v.idlist)
    #cache.delete_memoized(Board.idlist, v=v)
    cache.delete_memoized(frontlist, v=v)

    return "", 204
示例#18
0
def settings_unblock_user(v):

    user = get_user(request.values.get("username"))

    x = v.has_block(user)
    if not x:
        abort(409)

    g.db.delete(x)

    cache.delete_memoized(v.idlist)
    #cache.delete_memoized(Board.idlist, v=v)
    cache.delete_memoized(frontlist, v=v)

    return "", 204
示例#19
0
def follow_user(username, v):

    target = get_user(username)

    #check for existing follow
    if g.db.query(Follow).filter_by(user_id=v.id, target_id=target.id).first():
        abort(409)

    new_follow = Follow(user_id=v.id, target_id=target.id)

    g.db.add(new_follow)

    cache.delete_memoized(User.idlist, v, kind="user")

    return "", 204
示例#20
0
def unfollow_user(username, v):

    target = get_user(username)

    # check for existing follow
    follow = g.db.query(Follow).filter_by(user_id=v.id,
                                          target_id=target.id).first()

    if not follow:
        abort(409)

    g.db.delete(follow)

    cache.delete_memoized(User.idlist, v, kind="user")

    return "", 204
示例#21
0
def ban_post(post_id, v):

    post = g.db.query(Submission).filter_by(id=base36decode(post_id)).first()

    if not post:
        abort(400)

    post.is_banned = True
    post.is_approved = 0
    post.approved_utc = 0
    post.stickied = False
    post.ban_reason = request.form.get("reason", None)

    g.db.add(post)

    cache.delete_memoized(Board.idlist, post.board)

    return (redirect(post.permalink), post)
示例#22
0
def settings_block_guild(v):

    board = get_guild(request.values.get("board"), graceful=True)

    if not board:
        return jsonify({"error": "That guild doesn't exist."}), 404

    if v.has_blocked_guild(board):
        return jsonify({"error":
                        f"You have already blocked +{board.name}."}), 409

    new_block = BoardBlock(user_id=v.id,
                           board_id=board.id,
                           created_utc=int(time.time()))
    g.db.add(new_block)

    cache.delete_memoized(v.idlist)
    #cache.delete_memoized(Board.idlist, v=v)
    cache.delete_memoized(frontlist, v=v)

    return jsonify({"message": f"+{board.name} added to filter"})
示例#23
0
def follow_user(username, v):

    target = get_user(username)

    if target.id == v.id:
        return jsonify({"error": "You can't follow yourself!"}), 400

    # check for existing follow
    if g.db.query(Follow).filter_by(user_id=v.id, target_id=target.id).first():
        abort(409)

    new_follow = Follow(user_id=v.id, target_id=target.id)

    g.db.add(new_follow)
    g.db.flush()
    target.stored_subscriber_count = target.follower_count
    g.db.add(target)
    g.db.commit()

    cache.delete_memoized(User.idlist, v, kind="user")

    return "", 204
示例#24
0
def unsubscribe_board(boardname, v):

    board = get_guild(boardname)

    # check for existing subscription
    sub = g.db.query(Subscription).filter_by(user_id=v.id,
                                             board_id=board.id).first()

    if not sub or not sub.is_active:
        return jsonify({"error": f"You aren't a member of +{board.name}"}), 409

    sub.is_active = False

    g.db.add(sub)
    g.db.flush()

    # clear your cached guild listings
    cache.delete_memoized(User.idlist, v, kind="board")

    board.rank_trending = board.trending_rank
    board.stored_subscriber_count = board.subscriber_count
    g.db.add(board)

    return jsonify({"message": f"Left +{board.name}"}), 200
示例#25
0
def create_board_post(v):
    if not v.can_make_guild:
        return render_template("make_board.html",
                               title="Unable to make board",
                               error="You need more Reputation before you can make a Guild."
                               )

    board_name=request.form.get("name")
    board_name=board_name.lstrip("+")
    description = request.form.get("description")

    if not re.match(valid_board_regex, board_name):
        return render_template("make_board.html",
                               v=v,
                               error="Guild names must be 3-25 letters or numbers.",
                               description=description
                               )




    #check name
    if g.db.query(Board).filter(Board.name.ilike(board_name)).first():
        return render_template("make_board.html",
                               v=v,
                               error="That Guild already exists.",
                               description=description
                               )

    #check # recent boards made by user
    cutoff=int(time.time())-60*60*24
    recent=g.db.query(Board).filter(Board.creator_id==v.id, Board.created_utc >= cutoff).all()
    if len([x for x in recent])>=2:
        return render_template("message.html",
                               title="You need to wait a bit.",
                               message="You can only create up to 2 guilds per day. Try again later."
                               ), 429



    with CustomRenderer() as renderer:
        description_md=renderer.render(mistletoe.Document(description))
    description_html=sanitize(description_md, linkgen=True)

    #make the board

    new_board=Board(name=board_name,
                    description=description,
                    description_html=description_html,
                    over_18=bool(request.form.get("over_18","")),
                    creator_id=v.id
                    )

    g.db.add(new_board)

    g.db.commit()
    
    

    #add user as mod
    mod=ModRelationship(user_id=v.id,
                        board_id=new_board.id,
                        accepted=True)
    g.db.add(mod)

    #add subscription for user
    sub=Subscription(user_id=v.id,
                     board_id=new_board.id)
    g.db.add(sub)
    

    #clear cache
    cache.delete_memoized(guild_ids, sort="new")

    return redirect(new_board.permalink)
示例#26
0
def submit_post(v):

    title = request.form.get("title", "")

    url = request.form.get("url", "")

    board = get_guild(request.form.get('board', 'general'), graceful=True)
    if not board:
        board = get_guild('general')

    if re.match('^\s*$', title):
        return render_template("submit.html",
                               v=v,
                               error="Please enter a better title.",
                               title=title,
                               url=url,
                               body=request.form.get("body", ""),
                               b=board)

    # if len(title)<10:
    #     return render_template("submit.html",
    #                            v=v,
    #                            error="Please enter a better title.",
    #                            title=title,
    #                            url=url,
    #                            body=request.form.get("body",""),
    #                            b=board
    #                            )

    elif len(title) > 500:
        return render_template("submit.html",
                               v=v,
                               error="500 character limit for titles.",
                               title=title[0:500],
                               url=url,
                               body=request.form.get("body", ""),
                               b=board)

    parsed_url = urlparse(url)
    if not (parsed_url.scheme and parsed_url.netloc) and not request.form.get(
            "body") and not request.files.get("file", None):
        return render_template("submit.html",
                               v=v,
                               error="Please enter a URL or some text.",
                               title=title,
                               url=url,
                               body=request.form.get("body", ""),
                               b=board)
    #sanitize title
    title = sanitize(title, linkgen=False)

    #check for duplicate
    dup = db.query(Submission).filter_by(title=title,
                                         author_id=v.id,
                                         url=url,
                                         is_deleted=False,
                                         board_id=board.id).first()

    if dup:
        return redirect(dup.permalink)

    #check for domain specific rules

    parsed_url = urlparse(url)

    domain = parsed_url.netloc

    # check ban status
    domain_obj = get_domain(domain)
    if domain_obj:
        if not domain_obj.can_submit:
            return render_template("submit.html",
                                   v=v,
                                   error=BAN_REASONS[domain_obj.reason],
                                   title=title,
                                   url=url,
                                   body=request.form.get("body", ""),
                                   b=get_guild(request.form.get(
                                       "board", "general"),
                                               graceful=True))

        #check for embeds
        if domain_obj.embed_function:
            try:
                embed = eval(domain_obj.embed_function)(url)
            except:
                embed = ""
        else:
            embed = ""
    else:
        embed = ""

    #board
    board_name = request.form.get("board", "general")
    board_name = board_name.lstrip("+")
    board_name = board_name.rstrip()

    board = get_guild(board_name, graceful=True)

    if not board:
        board = get_guild('general')

    if board.is_banned:
        return render_template("submit.html",
                               v=v,
                               error=f"+{board.name} has been demolished.",
                               title=title,
                               url=url,
                               body=request.form.get("body", ""),
                               b=get_guild("general", graceful=True)), 403

    if board.has_ban(v):
        return render_template("submit.html",
                               v=v,
                               error=f"You are exiled from +{board.name}.",
                               title=title,
                               url=url,
                               body=request.form.get("body", ""),
                               b=get_guild("general")), 403

    if (board.restricted_posting
            or board.is_private) and not (board.can_submit(v)):
        return render_template(
            "submit.html",
            v=v,
            error=f"You are not an approved contributor for +{board.name}.",
            title=title,
            url=url,
            body=request.form.get("body", ""),
            b=get_guild(request.form.get("board", "general"), graceful=True))
    user_id = v.id
    user_name = v.username

    #Force https for submitted urls
    if request.form.get("url"):
        new_url = ParseResult(scheme="https",
                              netloc=parsed_url.netloc,
                              path=parsed_url.path,
                              params=parsed_url.params,
                              query=parsed_url.query,
                              fragment=parsed_url.fragment)
        url = urlunparse(new_url)
    else:
        url = ""

    #now make new post

    body = request.form.get("body", "")

    #catch too-long body
    if len(str(body)) > 10000:

        return render_template("submit.html",
                               v=v,
                               error="10000 character limit for text body",
                               title=title,
                               text=str(body)[0:10000],
                               url=url,
                               b=get_guild(request.form.get(
                                   "board", "general"),
                                           graceful=True)), 400

    if len(url) > 2048:

        return render_template("submit.html",
                               v=v,
                               error="URLs cannot be over 2048 characters",
                               title=title,
                               text=body[0:2000],
                               b=get_guild(request.form.get(
                                   "board", "general"),
                                           graceful=True)), 400

    with CustomRenderer() as renderer:
        body_md = renderer.render(mistletoe.Document(body))
    body_html = sanitize(body_md, linkgen=True)

    #check for embeddable video
    domain = parsed_url.netloc

    if url:
        repost = db.query(Submission).filter(
            Submission.url.ilike(url)).filter_by(
                board_id=board.id, is_deleted=False,
                is_banned=False).order_by(Submission.id.asc()).first()
    else:
        repost = None

    if request.files.get('file') and not v.can_submit_image:
        abort(403)

    new_post = Submission(
        title=title,
        url=url,
        author_id=user_id,
        body=body,
        body_html=body_html,
        embed_url=embed,
        domain_ref=domain_obj.id if domain_obj else None,
        board_id=board.id,
        original_board_id=board.id,
        over_18=(bool(request.form.get("over_18", "")) or board.over_18),
        post_public=not board.is_private,
        #author_name=user_name,
        #guild_name=board.name,
        repost_id=repost.id if repost else None)

    db.add(new_post)

    db.commit()

    new_post.determine_offensive()

    vote = Vote(user_id=user_id, vote_type=1, submission_id=new_post.id)
    db.add(vote)
    db.commit()

    #check for uploaded image
    if request.files.get('file'):

        file = request.files['file']

        name = f'post/{new_post.base36id}/{secrets.token_urlsafe(8)}'

        upload_file(name, file)

        #update post data
        new_post.url = f'https://{BUCKET}/{name}'
        new_post.is_image = True
        new_post.domain_ref = 1  #id of i.ruqqus.com domain
        db.add(new_post)
        db.commit()

    #spin off thumbnail generation and csam detection as  new threads
    elif new_post.url:
        new_thread = threading.Thread(target=thumbnail_thread,
                                      args=(new_post.base36id, ))
        new_thread.start()
        csam_thread = threading.Thread(target=check_csam, args=(new_post, ))
        csam_thread.start()

    #expire the relevant caches: front page new, board new
    cache.delete_memoized(frontlist, sort="new")
    cache.delete_memoized(Board.idlist, board, sort="new")

    #print(f"Content Event: @{new_post.author.username} post {new_post.base36id}")

    return redirect(new_post.permalink)
示例#27
0
def settings_profile_post(v):

    updated = False

    if request.values.get("over18", v.over_18) != v.over_18:
        updated = True
        v.over_18 = request.values.get("over18", None) == 'true'
        cache.delete_memoized(User.idlist, v)

    if request.values.get("hide_offensive",
                          v.hide_offensive) != v.hide_offensive:
        updated = True
        v.hide_offensive = request.values.get("hide_offensive", None) == 'true'
        cache.delete_memoized(User.idlist, v)

    if request.values.get("show_nsfl", v.show_nsfl) != v.show_nsfl:
        updated = True
        v.show_nsfl = request.values.get("show_nsfl", None) == 'true'
        cache.delete_memoized(User.idlist, v)

    if request.values.get("filter_nsfw", v.filter_nsfw) != v.filter_nsfw:
        updated = True
        v.filter_nsfw = not request.values.get("filter_nsfw", None) == 'true'
        cache.delete_memoized(User.idlist, v)

    if request.values.get("private", v.is_private) != v.is_private:
        updated = True
        v.is_private = request.values.get("private", None) == 'true'

    if request.values.get("nofollow", v.is_nofollow) != v.is_nofollow:
        updated = True
        v.is_nofollow = request.values.get("nofollow", None) == 'true'

    if request.values.get("bio") is not None:
        bio = request.values.get("bio")[0:256]

        if bio == v.bio:
            return render_template("settings_profile.html",
                                   v=v,
                                   error="You didn't change anything")

        v.bio = bio

        with CustomRenderer() as renderer:
            v.bio_html = renderer.render(mistletoe.Document(bio))
        v.bio_html = sanitize(v.bio_html, linkgen=True)
        g.db.add(v)
        return render_template("settings_profile.html",
                               v=v,
                               msg="Your bio has been updated.")

    x = request.values.get("title_id", None)
    if x:
        x = int(x)
        if x == 0:
            v.title_id = None
            updated = True
        elif x > 0:
            title = get_title(x)
            if bool(eval(title.qualification_expr)):
                v.title_id = title.id
                updated = True
            else:
                return jsonify({
                    "error":
                    f"You don't meet the requirements for title `{title.text}`."
                }), 403
        else:
            abort(400)

    if updated:
        g.db.add(v)

        return jsonify({"message": "Your settings have been updated."})

    else:
        return jsonify({"error": "You didn't change anything."}), 400
示例#28
0
文件: posts.py 项目: sicXnull/ruqqus
def submit_post(v):

    title = request.form.get("title", "")

    title = title.lstrip().rstrip()
    title = title.replace("\n", "")
    title = title.replace("\r", "")
    title = title.replace("\t", "")

    url = request.form.get("url", "")

    board = get_guild(request.form.get('board', 'general'), graceful=True)
    if not board:
        board = get_guild('general')

    if not title:
        return render_template("submit.html",
                               v=v,
                               error="Please enter a better title.",
                               title=title,
                               url=url,
                               body=request.form.get("body", ""),
                               b=board)

    # if len(title)<10:
    #     return render_template("submit.html",
    #                            v=v,
    #                            error="Please enter a better title.",
    #                            title=title,
    #                            url=url,
    #                            body=request.form.get("body",""),
    #                            b=board
    #                            )

    elif len(title) > 500:
        return render_template("submit.html",
                               v=v,
                               error="500 character limit for titles.",
                               title=title[0:500],
                               url=url,
                               body=request.form.get("body", ""),
                               b=board)

    parsed_url = urlparse(url)
    if not (parsed_url.scheme and parsed_url.netloc) and not request.form.get(
            "body") and not request.files.get("file", None):
        return render_template("submit.html",
                               v=v,
                               error="Please enter a URL or some text.",
                               title=title,
                               url=url,
                               body=request.form.get("body", ""),
                               b=board)
    #sanitize title
    title = bleach.clean(title)

    #Force https for submitted urls
    if request.form.get("url"):
        new_url = ParseResult(scheme="https",
                              netloc=parsed_url.netloc,
                              path=parsed_url.path,
                              params=parsed_url.params,
                              query=parsed_url.query,
                              fragment=parsed_url.fragment)
        url = urlunparse(new_url)
    else:
        url = ""

    body = request.form.get("body", "")
    #check for duplicate
    dup = g.db.query(Submission).join(Submission.submission_aux).filter(
        Submission.author_id == v.id, Submission.is_deleted == False,
        Submission.board_id == board.id, SubmissionAux.title == title,
        SubmissionAux.url == url, SubmissionAux.body == body).first()

    if dup:
        return redirect(dup.permalink)

    #check for domain specific rules

    parsed_url = urlparse(url)

    domain = parsed_url.netloc

    # check ban status
    domain_obj = get_domain(domain)
    if domain_obj:
        if not domain_obj.can_submit:
            return render_template("submit.html",
                                   v=v,
                                   error=BAN_REASONS[domain_obj.reason],
                                   title=title,
                                   url=url,
                                   body=request.form.get("body", ""),
                                   b=get_guild(request.form.get(
                                       "board", "general"),
                                               graceful=True))

        #check for embeds
        if domain_obj.embed_function:
            try:
                embed = eval(domain_obj.embed_function)(url)
            except:
                embed = ""
        else:
            embed = ""
    else:
        embed = ""

    #board
    board_name = request.form.get("board", "general")
    board_name = board_name.lstrip("+")
    board_name = board_name.rstrip()

    board = get_guild(board_name, graceful=True)

    if not board:
        board = get_guild('general')

    if board.is_banned:
        return render_template("submit.html",
                               v=v,
                               error=f"+{board.name} has been demolished.",
                               title=title,
                               url=url,
                               body=request.form.get("body", ""),
                               b=get_guild("general", graceful=True)), 403

    if board.has_ban(v):
        return render_template("submit.html",
                               v=v,
                               error=f"You are exiled from +{board.name}.",
                               title=title,
                               url=url,
                               body=request.form.get("body", ""),
                               b=get_guild("general")), 403

    if (board.restricted_posting
            or board.is_private) and not (board.can_submit(v)):
        return render_template(
            "submit.html",
            v=v,
            error=f"You are not an approved contributor for +{board.name}.",
            title=title,
            url=url,
            body=request.form.get("body", ""),
            b=get_guild(request.form.get("board", "general"), graceful=True))

    #similarity check
    now = int(time.time())
    cutoff = now - 60 * 60 * 24
    similar_posts = g.db.query(Submission).options(lazyload('*')).join(
        Submission.submission_aux).filter(
            Submission.author_id == v.id,
            SubmissionAux.title.op('<->')(title) <
            app.config["SPAM_SIMILARITY_THRESHOLD"],
            Submission.created_utc > cutoff).all()

    if url:
        similar_urls = g.db.query(Submission).options(lazyload('*')).join(
            Submission.submission_aux).filter(
                Submission.author_id == v.id,
                SubmissionAux.url.op('<->')(url) <
                app.config["SPAM_URL_SIMILARITY_THRESHOLD"],
                Submission.created_utc > cutoff).all()
    else:
        similar_urls = []

    threshold = app.config["SPAM_SIMILAR_COUNT_THRESHOLD"]
    if v.age >= (60 * 60 * 24 * 30):
        threshold *= 4
    elif v.age >= (60 * 60 * 24 * 7):
        threshold *= 3
    elif v.age >= (60 * 60 * 24):
        threshold *= 2

    if max(len(similar_urls), len(similar_posts)) >= threshold:

        text = "Your Ruqqus account has been suspended for 1 day for the following reason:\n\n> Too much spam!"
        send_notification(v, text)

        v.ban(reason="Spamming.", include_alts=True, days=1)

        for post in similar_posts + similar_urls:
            post.is_banned = True
            post.ban_reason = "Automatic spam removal. This happened because the post's creator submitted too much similar content too quickly."
            g.db.add(post)

        g.db.commit()
        return redirect("/notifications")

    #catch too-long body
    if len(str(body)) > 10000:

        return render_template("submit.html",
                               v=v,
                               error="10000 character limit for text body",
                               title=title,
                               text=str(body)[0:10000],
                               url=url,
                               b=get_guild(request.form.get(
                                   "board", "general"),
                                           graceful=True)), 400

    if len(url) > 2048:

        return render_template("submit.html",
                               v=v,
                               error="URLs cannot be over 2048 characters",
                               title=title,
                               text=body[0:2000],
                               b=get_guild(request.form.get(
                                   "board", "general"),
                                           graceful=True)), 400

    #render text

    with CustomRenderer() as renderer:
        body_md = renderer.render(mistletoe.Document(body))
    body_html = sanitize(body_md, linkgen=True)

    ##check spam
    soup = BeautifulSoup(body_html, features="html.parser")
    links = [x['href'] for x in soup.find_all('a') if x.get('href')]

    if url:
        links = [url] + links

    for link in links:
        parse_link = urlparse(link)
        check_url = ParseResult(scheme="https",
                                netloc=parse_link.netloc,
                                path=parse_link.path,
                                params=parse_link.params,
                                query=parse_link.query,
                                fragment='')
        check_url = urlunparse(check_url)

        badlink = g.db.query(BadLink).filter(
            literal(check_url).contains(BadLink.link)).first()
        if badlink:
            if badlink.autoban:
                text = "Your Ruqqus account has been suspended for 1 day for the following reason:\n\n> Too much spam!"
                send_notification(v, text)
                v.ban(days=1, reason="spam")

                return redirect('/notifications')
            else:
                return render_template(
                    "submit.html",
                    v=v,
                    error=
                    f"The link `{badlink.link}` is not allowed. Reason: {badlink.reason}",
                    title=title,
                    text=body[0:2000],
                    b=get_guild(request.form.get("board", "general"),
                                graceful=True)), 400

    #check for embeddable video
    domain = parsed_url.netloc

    if url:
        repost = g.db.query(Submission).join(Submission.submission_aux).filter(
            SubmissionAux.url.ilike(url), Submission.board_id == board.id,
            Submission.is_deleted == False,
            Submission.is_banned == False).order_by(
                Submission.id.asc()).first()
    else:
        repost = None

    if request.files.get('file') and not v.can_submit_image:
        abort(403)

    #offensive
    is_offensive = False
    for x in g.db.query(BadWord).all():
        if (body and x.check(body)) or x.check(title):
            is_offensive = True
            break

    new_post = Submission(author_id=v.id,
                          domain_ref=domain_obj.id if domain_obj else None,
                          board_id=board.id,
                          original_board_id=board.id,
                          over_18=(bool(request.form.get("over_18", ""))
                                   or board.over_18),
                          post_public=not board.is_private,
                          repost_id=repost.id if repost else None,
                          is_offensive=is_offensive)

    g.db.add(new_post)
    g.db.flush()

    new_post_aux = SubmissionAux(id=new_post.id,
                                 url=url,
                                 body=body,
                                 body_html=body_html,
                                 embed_url=embed,
                                 title=title)
    g.db.add(new_post_aux)
    g.db.flush()

    vote = Vote(user_id=v.id, vote_type=1, submission_id=new_post.id)
    g.db.add(vote)
    g.db.flush()

    g.db.commit()
    g.db.refresh(new_post)

    #check for uploaded image
    if request.files.get('file'):

        file = request.files['file']

        name = f'post/{new_post.base36id}/{secrets.token_urlsafe(8)}'
        upload_file(name, file)

        #thumb_name=f'posts/{new_post.base36id}/thumb.png'
        #upload_file(name, file, resize=(375,227))

        #update post data
        new_post.url = f'https://{BUCKET}/{name}'
        new_post.is_image = True
        new_post.domain_ref = 1  #id of i.ruqqus.com domain
        g.db.add(new_post)
        g.db.commit()

    #spin off thumbnail generation and csam detection as  new threads
    if new_post.url or request.files.get('file'):
        new_thread = threading.Thread(target=thumbnail_thread,
                                      args=(new_post.base36id, ))
        new_thread.start()
        csam_thread = threading.Thread(target=check_csam, args=(new_post, ))
        csam_thread.start()

    #expire the relevant caches: front page new, board new
    #cache.delete_memoized(frontlist, sort="new")
    g.db.commit()
    cache.delete_memoized(Board.idlist, board, sort="new")

    #print(f"Content Event: @{new_post.author.username} post {new_post.base36id}")

    return {
        "html": lambda: redirect(new_post.permalink),
        "api": lambda: jsonify(new_post.json)
    }
示例#29
0
文件: posts.py 项目: ruqqus/ruqqus
def submit_post(v):

    title = request.form.get("title", "").lstrip().rstrip()

    title = title.lstrip().rstrip()
    title = title.replace("\n", "")
    title = title.replace("\r", "")
    title = title.replace("\t", "")

    url = request.form.get("url", "")

    board = get_guild(request.form.get('board', 'general'), graceful=True)
    if not board:
        board = get_guild('general')

    if not title:
        return {
            "html":
            lambda: (render_template("submit.html",
                                     v=v,
                                     error="Please enter a better title.",
                                     title=title,
                                     url=url,
                                     body=request.form.get("body", ""),
                                     b=board), 400),
            "api":
            lambda: ({
                "error": "Please enter a better title"
            }, 400)
        }

    # if len(title)<10:
    #     return render_template("submit.html",
    #                            v=v,
    #                            error="Please enter a better title.",
    #                            title=title,
    #                            url=url,
    #                            body=request.form.get("body",""),
    #                            b=board
    #                            )

    elif len(title) > 500:
        return {
            "html":
            lambda: (render_template("submit.html",
                                     v=v,
                                     error="500 character limit for titles.",
                                     title=title[0:500],
                                     url=url,
                                     body=request.form.get("body", ""),
                                     b=board), 400),
            "api":
            lambda: ({
                "error": "500 character limit for titles"
            }, 400)
        }

    parsed_url = urlparse(url)
    if not (parsed_url.scheme and parsed_url.netloc) and not request.form.get(
            "body") and not request.files.get("file", None):
        return {
            "html":
            lambda: (render_template("submit.html",
                                     v=v,
                                     error="Please enter a url or some text.",
                                     title=title,
                                     url=url,
                                     body=request.form.get("body", ""),
                                     b=board), 400),
            "api":
            lambda: ({
                "error": "`url` or `body` parameter required."
            }, 400)
        }
    # sanitize title
    title = bleach.clean(title, tags=[])

    # Force https for submitted urls

    if request.form.get("url"):
        new_url = ParseResult(scheme="https",
                              netloc=parsed_url.netloc,
                              path=parsed_url.path,
                              params=parsed_url.params,
                              query=parsed_url.query,
                              fragment=parsed_url.fragment)
        url = urlunparse(new_url)
    else:
        url = ""

    body = request.form.get("body", "")
    # check for duplicate
    dup = g.db.query(Submission).join(Submission.submission_aux).filter(
        Submission.author_id == v.id, Submission.deleted_utc == 0,
        Submission.board_id == board.id, SubmissionAux.title == title,
        SubmissionAux.url == url, SubmissionAux.body == body).first()

    if dup:
        return redirect(dup.permalink)

    # check for domain specific rules

    parsed_url = urlparse(url)

    domain = parsed_url.netloc

    # check ban status
    domain_obj = get_domain(domain)
    if domain_obj:
        if not domain_obj.can_submit:

            if domain_obj.reason == 4:
                v.ban(days=30, reason="Digitally malicious content")
            elif domain_obj.reason == 7:
                v.ban(reason="Sexualizing minors")

            return {
                "html":
                lambda: (render_template("submit.html",
                                         v=v,
                                         error=BAN_REASONS[domain_obj.reason],
                                         title=title,
                                         url=url,
                                         body=request.form.get("body", ""),
                                         b=board), 400),
                "api":
                lambda: ({
                    "error": BAN_REASONS[domain_obj.reason]
                }, 400)
            }

        # check for embeds
        if domain_obj.embed_function:
            try:
                embed = eval(domain_obj.embed_function)(url)
            except BaseException:
                embed = ""
        else:
            embed = ""
    else:

        embed = ""

    # board
    board_name = request.form.get("board", "general")
    board_name = board_name.lstrip("+")
    board_name = board_name.rstrip()

    board = get_guild(board_name, graceful=True)

    if not board:

        return {
            "html":
            lambda:
            (render_template("submit.html",
                             v=v,
                             error=f"Please enter a Guild to submit to.",
                             title=title,
                             url=url,
                             body=request.form.get("body", ""),
                             b=None), 403),
            "api":
            lambda: (jsonify(
                {"error": f"403 Forbidden - +{board.name} has been banned."}))
        }

    if board.is_banned:

        return {
            "html":
            lambda: (render_template("submit.html",
                                     v=v,
                                     error=f"+{board.name} has been banned.",
                                     title=title,
                                     url=url,
                                     body=request.form.get("body", ""),
                                     b=None), 403),
            "api":
            lambda: (jsonify(
                {"error": f"403 Forbidden - +{board.name} has been banned."}))
        }

    if board.has_ban(v):
        return {
            "html":
            lambda:
            (render_template("submit.html",
                             v=v,
                             error=f"You are exiled from +{board.name}.",
                             title=title,
                             url=url,
                             body=request.form.get("body", ""),
                             b=None), 403),
            "api":
            lambda: (jsonify({
                "error":
                f"403 Not Authorized - You are exiled from +{board.name}"
            }), 403)
        }

    if (board.restricted_posting
            or board.is_private) and not (board.can_submit(v)):
        return {
            "html":
            lambda: (render_template(
                "submit.html",
                v=v,
                error=
                f"You are not an approved contributor for +{board.name}.",
                title=title,
                url=url,
                body=request.form.get("body", ""),
                b=None), 403),
            "api":
            lambda: (jsonify({
                "error":
                f"403 Not Authorized - You are not an approved contributor for +{board.name}"
            }), 403)
        }

    if board.disallowbots and request.headers.get("X-User-Type") == "Bot":
        return {
            "api":
            lambda: (jsonify({
                "error":
                f"403 Not Authorized - +{board.name} disallows bots from posting and commenting!"
            }), 403)
        }

    # similarity check
    now = int(time.time())
    cutoff = now - 60 * 60 * 24

    similar_posts = g.db.query(Submission).options(lazyload('*')).join(
        Submission.submission_aux
    ).filter(
        #or_(
        #    and_(
        Submission.author_id == v.id,
        SubmissionAux.title.op('<->')(title) <
        app.config["SPAM_SIMILARITY_THRESHOLD"],
        Submission.created_utc > cutoff
        #    ),
        #    and_(
        #        SubmissionAux.title.op('<->')(title) < app.config["SPAM_SIMILARITY_THRESHOLD"]/2,
        #        Submission.created_utc > cutoff
        #    )
        #)
    ).all()

    if url:
        similar_urls = g.db.query(Submission).options(lazyload('*')).join(
            Submission.submission_aux
        ).filter(
            #or_(
            #    and_(
            Submission.author_id == v.id,
            SubmissionAux.url.op('<->')(url) <
            app.config["SPAM_URL_SIMILARITY_THRESHOLD"],
            Submission.created_utc > cutoff
            #    ),
            #    and_(
            #        SubmissionAux.url.op('<->')(url) < app.config["SPAM_URL_SIMILARITY_THRESHOLD"]/2,
            #        Submission.created_utc > cutoff
            #    )
            #)
        ).all()
    else:
        similar_urls = []

    threshold = app.config["SPAM_SIMILAR_COUNT_THRESHOLD"]
    if v.age >= (60 * 60 * 24 * 7):
        threshold *= 3
    elif v.age >= (60 * 60 * 24):
        threshold *= 2

    if max(len(similar_urls), len(similar_posts)) >= threshold:

        text = "Your Ruqqus account has been suspended for 1 day for the following reason:\n\n> Too much spam!"
        send_notification(v, text)

        v.ban(reason="Spamming.", days=1)

        for alt in v.alts:
            if not alt.is_suspended:
                alt.ban(reason="Spamming.", days=1)

        for post in similar_posts + similar_urls:
            post.is_banned = True
            post.is_pinned = False
            post.ban_reason = "Automatic spam removal. This happened because the post's creator submitted too much similar content too quickly."
            g.db.add(post)
            ma = ModAction(user_id=1,
                           target_submission_id=post.id,
                           kind="ban_post",
                           board_id=post.board_id,
                           note="spam")
            g.db.add(ma)
        g.db.commit()
        return redirect("/notifications")

    # catch too-long body
    if len(str(body)) > 10000:

        return {
            "html":
            lambda:
            (render_template("submit.html",
                             v=v,
                             error="10000 character limit for text body.",
                             title=title,
                             url=url,
                             body=request.form.get("body", ""),
                             b=board), 400),
            "api":
            lambda: ({
                "error": "10000 character limit for text body."
            }, 400)
        }

    if len(url) > 2048:

        return {
            "html":
            lambda: (render_template("submit.html",
                                     v=v,
                                     error="2048 character limit for URLs.",
                                     title=title,
                                     url=url,
                                     body=request.form.get("body", ""),
                                     b=board), 400),
            "api":
            lambda: ({
                "error": "2048 character limit for URLs."
            }, 400)
        }

    # render text

    body = preprocess(body)

    with CustomRenderer() as renderer:
        body_md = renderer.render(mistletoe.Document(body))
    body_html = sanitize(body_md, linkgen=True)

    # Run safety filter
    bans = filter_comment_html(body_html)
    if bans:
        ban = bans[0]
        reason = f"Remove the {ban.domain} link from your post and try again."
        if ban.reason:
            reason += f" {ban.reason_text}"

        #auto ban for digitally malicious content
        if any([x.reason == 4 for x in bans]):
            v.ban(days=30,
                  reason="Digitally malicious content is not allowed.")
            abort(403)

        return {
            "html":
            lambda: (render_template("submit.html",
                                     v=v,
                                     error=reason,
                                     title=title,
                                     url=url,
                                     body=request.form.get("body", ""),
                                     b=board), 403),
            "api":
            lambda: ({
                "error": reason
            }, 403)
        }

    # check spam
    soup = BeautifulSoup(body_html, features="html.parser")
    links = [x['href'] for x in soup.find_all('a') if x.get('href')]

    if url:
        links = [url] + links

    for link in links:
        parse_link = urlparse(link)
        check_url = ParseResult(scheme="https",
                                netloc=parse_link.netloc,
                                path=parse_link.path,
                                params=parse_link.params,
                                query=parse_link.query,
                                fragment='')
        check_url = urlunparse(check_url)

        badlink = g.db.query(BadLink).filter(
            literal(check_url).contains(BadLink.link)).first()
        if badlink:
            if badlink.autoban:
                text = "Your Ruqqus account has been suspended for 1 day for the following reason:\n\n> Too much spam!"
                send_notification(v, text)
                v.ban(days=1, reason="spam")

                return redirect('/notifications')
            else:

                return {
                    "html":
                    lambda: (render_template(
                        "submit.html",
                        v=v,
                        error=
                        f"The link `{badlink.link}` is not allowed. Reason: {badlink.reason}.",
                        title=title,
                        url=url,
                        body=request.form.get("body", ""),
                        b=board), 400),
                    "api":
                    lambda: ({
                        "error":
                        f"The link `{badlink.link}` is not allowed. Reason: {badlink.reason}"
                    }, 400)
                }

    # check for embeddable video
    domain = parsed_url.netloc

    if url:
        repost = g.db.query(Submission).join(Submission.submission_aux).filter(
            SubmissionAux.url.ilike(url), Submission.board_id == board.id,
            Submission.deleted_utc == 0,
            Submission.is_banned == False).order_by(
                Submission.id.asc()).first()
    else:
        repost = None

    if repost and request.values.get("no_repost"):
        return redirect(repost.permalink)

    if request.files.get('file') and not v.can_submit_image:
        abort(403)

    # offensive
    is_offensive = False
    for x in g.db.query(BadWord).all():
        if (body and x.check(body)) or x.check(title):
            is_offensive = True
            break

    new_post = Submission(author_id=v.id,
                          domain_ref=domain_obj.id if domain_obj else None,
                          board_id=board.id,
                          original_board_id=board.id,
                          over_18=(bool(request.form.get("over_18", ""))
                                   or board.over_18),
                          post_public=not board.is_private,
                          repost_id=repost.id if repost else None,
                          is_offensive=is_offensive,
                          app_id=v.client.application.id if v.client else None,
                          creation_region=request.headers.get("cf-ipcountry"),
                          is_bot=request.headers.get("X-User-Type",
                                                     "").lower() == "bot")

    g.db.add(new_post)
    g.db.flush()

    new_post_aux = SubmissionAux(id=new_post.id,
                                 url=url,
                                 body=body,
                                 body_html=body_html,
                                 embed_url=embed,
                                 title=title)
    g.db.add(new_post_aux)
    g.db.flush()

    vote = Vote(user_id=v.id, vote_type=1, submission_id=new_post.id)
    g.db.add(vote)
    g.db.flush()

    g.db.refresh(new_post)

    # check for uploaded image
    if request.files.get('file'):

        #check file size
        if request.content_length > 16 * 1024 * 1024 and not v.has_premium:
            g.db.rollback()
            abort(413)

        file = request.files['file']
        if not file.content_type.startswith('image/'):
            return {
                "html":
                lambda: (render_template("submit.html",
                                         v=v,
                                         error=f"Image files only.",
                                         title=title,
                                         body=request.form.get("body", ""),
                                         b=board), 400),
                "api":
                lambda: ({
                    "error": f"Image files only"
                }, 400)
            }

        name = f'post/{new_post.base36id}/{secrets.token_urlsafe(8)}'
        upload_file(name, file)

        # thumb_name=f'posts/{new_post.base36id}/thumb.png'
        #upload_file(name, file, resize=(375,227))

        # update post data
        new_post.url = f'https://{BUCKET}/{name}'
        new_post.is_image = True
        new_post.domain_ref = 1  # id of i.ruqqus.com domain
        g.db.add(new_post)
        g.db.add(new_post.submission_aux)
        g.db.commit()

        #csam detection
        def del_function():
            db = db_session()
            delete_file(name)
            new_post.is_banned = True
            db.add(new_post)
            db.commit()
            ma = ModAction(kind="ban_post",
                           user_id=1,
                           note="banned image",
                           target_submission_id=new_post.id)
            db.add(ma)
            db.commit()
            db.close()

        csam_thread = threading.Thread(target=check_csam_url,
                                       args=(f"https://{BUCKET}/{name}", v,
                                             del_function))
        csam_thread.start()

    g.db.commit()

    # spin off thumbnail generation and csam detection as  new threads
    if (new_post.url or request.files.get('file')) and (
            v.is_activated or request.headers.get('cf-ipcountry') != "T1"):
        new_thread = gevent.spawn(thumbnail_thread, new_post.base36id)

    # expire the relevant caches: front page new, board new
    cache.delete_memoized(frontlist)
    g.db.commit()
    cache.delete_memoized(Board.idlist, board, sort="new")

    # queue up notifications for username mentions
    notify_users = set()

    soup = BeautifulSoup(body_html, features="html.parser")
    for mention in soup.find_all("a", href=re.compile("^/@(\w+)"), limit=3):
        username = mention["href"].split("@")[1]
        user = g.db.query(User).filter_by(username=username).first()
        if user and not v.any_block_exists(user) and user.id != v.id:
            notify_users.add(user.id)

    for x in notify_users:
        send_notification(
            x,
            f"@{v.username} has mentioned you: https://ruqqus.com{new_post.permalink}"
        )

    # print(f"Content Event: @{new_post.author.username} post
    # {new_post.base36id}")

    #Bell notifs

    board_uids = g.db.query(Subscription.user_id).options(
        lazyload('*')).filter(
            Subscription.board_id == new_post.board_id,
            Subscription.is_active == True, Subscription.get_notifs == True,
            Subscription.user_id != v.id,
            Subscription.user_id.notin_(
                g.db.query(
                    UserBlock.user_id).filter_by(target_id=v.id).subquery()))

    follow_uids = g.db.query(Follow.user_id).options(lazyload('*')).filter(
        Follow.target_id == v.id, Follow.get_notifs == True,
        Follow.user_id != v.id,
        Follow.user_id.notin_(
            g.db.query(
                UserBlock.user_id).filter_by(target_id=v.id).subquery()),
        Follow.user_id.notin_(
            g.db.query(UserBlock.target_id).filter_by(
                user_id=v.id).subquery())).join(Follow.target).filter(
                    User.is_private == False,
                    User.is_nofollow == False,
                )

    if not new_post.is_public:

        contribs = g.db.query(ContributorRelationship).filter_by(
            board_id=new_post.board_id, is_active=True).subquery()
        mods = g.db.query(ModRelationship).filter_by(
            board_id=new_post.board_id, accepted=True).subquery()

        board_uids = board.uids.join(
            contribs, contribs.c.user_id == Subscription.user_id,
            isouter=True).join(mods,
                               mods.c.user_id == Subscription.user_id,
                               isouter=True).filter(
                                   or_(mods.c.id != None,
                                       contribs.c.id != None))

        follow_uids = follow_uids.join(contribs,
                                       contribs.c.user_id == Follow.user_id,
                                       isouter=True).join(
                                           mods,
                                           mods.c.user_id == Follow.user_id,
                                           isouter=True).filter(
                                               or_(mods.c.id != None,
                                                   contribs.c.id != None))

    uids = list(
        set([x[0]
             for x in board_uids.all()] + [x[0] for x in follow_uids.all()]))

    for uid in uids:
        new_notif = Notification(user_id=uid, submission_id=new_post.id)
        g.db.add(new_notif)
    g.db.commit()

    return {
        "html": lambda: redirect(new_post.permalink),
        "api": lambda: jsonify(new_post.json)
    }
示例#30
0
def settings_profile_post(v):

    updated = False

    if request.form.get("new_password"):
        if request.form.get("new_password") != request.form.get(
                "cnf_password"):
            return render_template("settings.html",
                                   v=v,
                                   error="Passwords do not match.")

        if not v.verifyPass(request.form.get("old_password")):
            return render_template("settings.html",
                                   v=v,
                                   error="Incorrect password")

        v.passhash = v.hash_password(request.form.get("new_password"))
        updated = True

    if request.form.get("over18") != v.over_18:
        updated = True
        v.over_18 = bool(request.form.get("over18", None))
        cache.delete_memoized(User.idlist, v)

    if request.form.get("hide_offensive") != v.hide_offensive:
        updated = True
        v.hide_offensive = bool(request.form.get("hide_offensive", None))
        cache.delete_memoized(User.idlist, v)

    if request.form.get("show_nsfl") != v.show_nsfl:
        updated = True
        v.show_nsfl = bool(request.form.get("show_nsfl", None))
        cache.delete_memoized(User.idlist, v)

    if request.form.get("private") != v.is_private:
        updated = True
        v.is_private = bool(request.form.get("private", None))

    if request.form.get("bio") != v.bio:
        updated = True
        bio = request.form.get("bio")[0:256]
        v.bio = bio

        with CustomRenderer() as renderer:
            v.bio_html = renderer.render(mistletoe.Document(bio))
        v.bio_html = sanitize(v.bio_html, linkgen=True)

    x = int(request.form.get("title_id", 0))
    if x == 0:
        v.title_id = None
        updated = True
    elif x > 0:
        title = get_title(x)
        if bool(eval(title.qualification_expr)):
            v.title_id = title.id
            updated = True
        else:
            return render_template(
                "settings_profile.html",
                v=v,
                error=
                f"Unable to set title {title.text} - {title.requirement_string}"
            )
    else:
        abort(400)

    if updated:
        g.db.add(v)

        return render_template("settings_profile.html",
                               v=v,
                               msg="Your settings have been saved.")

    else:
        return render_template("settings_profile.html",
                               v=v,
                               error="You didn't change anything.")