def comment(): tid = request.args.get('tid') form = ReplyForm() if request.method == 'GET': pid = request.args.get('pid') pn = request.args.get('pn') if not pn: pn = 1 else: pn = int(pn) if pn == 1: comment = Comment.query.filter_by(tid=tid).first() else: comment = None title = Comment.query.filter_by(tid=tid).first().title reply_list = Reply.query.filter_by( tid=tid).order_by('reply.rid').paginate(pn, MAX_REPLY_NUM_ONE_PAGE) return render_template('comment.html', pid = pid, tid = tid, title = title, comment = comment, pn = pn, \ reply_list = reply_list, form = form) else: reply = Reply(tid = tid, userid = current_user.userid, nickname = current_user.nickname, \ content = form.content.data, post_time = get_now_time()) comment = Comment.query.filter_by(tid=tid).first() comment.re += 1 db.session.add(reply) db.session.commit() return redirect(request.referrer)
def add_reply(source: Source, journalist: Journalist, journalist_who_saw: Optional[Journalist]) -> None: """ Adds a single reply to a source. """ record_source_interaction(source) fname = "{}-{}-reply.gpg".format(source.interaction_count, source.journalist_filename) EncryptionManager.get_default().encrypt_journalist_reply( for_source_with_filesystem_id=source.filesystem_id, reply_in=next(replies), encrypted_reply_path_out=Path(Storage.get_default().path( source.filesystem_id, fname)), ) reply = Reply(journalist, source, fname, Storage.get_default()) db.session.add(reply) # Journalist who replied has seen the reply author_seen_reply = SeenReply(reply=reply, journalist=journalist) db.session.add(author_seen_reply) if journalist_who_saw: other_seen_reply = SeenReply(reply=reply, journalist=journalist_who_saw) db.session.add(other_seen_reply) db.session.commit()
def getReplyWrite(self, params, thread): reply_post = self.thread_data["posts"][1:] replies = [] total_posts = len(reply_post) if params.total_posts: total_posts = min(params.total_posts, len(reply_post)) for i in range(0, total_posts): reply = reply_post[i] if "tim" in reply.keys(): reply["img_src"] = "https://i.4cdn.org/{}/{}{}".format( thread.board, reply["tim"], reply["ext"]) reply_img_text = "{}{}".format(reply["filename"], reply["ext"]) if params.preserve: self.download(reply["img_src"], reply_img_text, params) reply["img_src"] = '{}/{}'.format(thread.tid, reply_img_text) if params.verbose: print("Downloading reply:", reply["no"], "replied on", reply["now"]) reply["board"] = thread.board reply_info = Reply(reply) replies.append(reply_info) self.db.insert_reply(reply_info) return replies
def add_reply( source: Source, journalist: Journalist, journalist_who_saw: Optional[Journalist] ) -> None: """ Adds a single reply to a source. """ record_source_interaction(source) fname = "{}-{}-reply.gpg".format(source.interaction_count, source.journalist_filename) current_app.crypto_util.encrypt( next(replies), [ current_app.crypto_util.get_fingerprint(source.filesystem_id), config.JOURNALIST_KEY, ], current_app.storage.path(source.filesystem_id, fname), ) reply = Reply(journalist, source, fname) db.session.add(reply) db.session.flush() # Journalist who replied has seen the reply author_seen_reply = SeenReply(reply_id=reply.id, journalist_id=journalist.id) db.session.add(author_seen_reply) if journalist_who_saw: other_seen_reply = SeenReply(reply_id=reply.id, journalist_id=journalist_who_saw.id) db.session.add(other_seen_reply) db.session.commit()
async def submit_reply(event, ctx, user, *args, **kwargs): data = event.body replier_uid = str(user['_id']) rid = 'r' + str(uuid.uuid4())[:13] qid = data.qid q_m = QuestionModel.get_maybe(qid) if q_m == Nothing: raise LambdaError(404, 'question not found') body = data.body if not (0 < len(body) <= 4000): raise Exception("Your question is too large!") ts = datetime.datetime.now() parent_rid = data.get('parent_rid', None) child_rids = list() is_staff = await has_role('qanda_staff', user['_id']) r = Reply(rid=rid, qid=qid, uid=replier_uid, body=body, ts=ts, parent_rid=parent_rid, child_rids=child_rids, is_staff=is_staff, display_name=gen_display_name(user, data.display_choice)) r.save() update_actions = [ ReplyIdsByQid.rids.set( (ReplyIdsByQid.rids | []).prepend([GenericPointer(ts=ts, id=rid)])) ] ReplyIdsByQid(qid="global").update(actions=update_actions) ReplyIdsByQid(qid=qid).update(actions=update_actions) ReplyIdsByUid(uid=replier_uid).update(actions=update_actions) await email_notify_new_reply(replier_uid, r, q_m.getValue()) return {'reply': r.to_python(), 'submitted': True, 'rid': rid}
def reply_letter(user_id, letter_id): import datetime error = None letter = db.session.query(LetterToJuliet).filter_by(id=letter_id) form = NewLetter(request.form) if request.method == 'POST': if form.validate_on_submit(): letter.first().reply_status = 1 reply_letter = Reply(user_id, form.title.data, form.content.data, datetime.date.today(), letter_id) db.session.add(reply_letter) db.session.commit() flash('The letter was reply') return redirect(url_for('user_page', user_id=user_id)) else: return render_template('reply_letter.html', letters=letter, user_id=user_id, letter_id=letter_id, form=form, error=error) #may need modified if request.method == 'GET': return render_template('reply_letter.html', letters=letter, user_id=user_id, letter_id=letter_id, form=form)
def getOP(self, page_soup, params, thread): op_post = page_soup.find_all("div", {"class": "postContainer opContainer"}) op_message = op_post[0].find_all("blockquote", {"class": "postMessage"})[0] op_img = op_post[0].find_all("div", {"class": "fileText"}) op_img_src = '' op_img_text = '' if len(op_img) > 0: op_img_src = op_img[0].find("a")["href"] op_img_text = op_img[0].find("a").text op_img_src = 'https:{}'.format(op_img_src) if params.preserve: self.download(op_img_src, op_img_text, params) op_img_src = '{}/{}'.format(thread.tid, op_img_text) op_subject = op_post[0].find_all("span", {"class": "subject"})[1].text op_name = op_post[0].find_all("span", {"class": "name"})[0].text op_date = op_post[0].find_all( "span", {"class": "dateTime"})[0].text.split("No")[0] op_pid = op_post[0].find_all("div", {"class": "post op"})[0]['id'][1:] if params.verbose: print("Downloading post:", op_pid, "posted on", op_date[:-9]) p1 = Reply(op_name, op_date, op_message, op_pid, op_img_src, op_img_text, op_subject) return p1
def show_comment(tid): form = ReplyForm() if request.method == 'GET': page = request.args.get('page') if not page: page = 1 else: page = int(page) if page == 1: comment = Comment.query.get(tid) else: comment = None title = Comment.query.get(tid).title reply_list = Reply.query.filter_by(tid=tid).order_by( Reply.tid).paginate(page, MAX_REPLY_NUM_ONE_PAGE, False) return render_template('showcomment.html', tid=tid, title=title, comment=comment, page=page, reply_list=reply_list, form=form) else: reply = Reply(tid = tid, userid = current_user.userid, nickname = current_user.nickname, \ content = form.content.data, post_time = get_now_time()) comment = Comment.query.get(tid) comment.re += 1 reply.save() db.session.commit() return redirect(request.referrer)
def reply() -> werkzeug.Response: """Attempt to send a Reply from a Journalist to a Source. Empty messages are rejected, and an informative error message is flashed on the client. In the case of unexpected errors involving database transactions (potentially caused by racing request threads that modify the same the database object) logging is done in such a way so as not to write potentially sensitive information to disk, and a generic error message is flashed on the client. Returns: flask.Response: The user is redirected to the same Source collection view, regardless if the Reply is created successfully. """ form = ReplyForm() if not form.validate_on_submit(): for error in form.message.errors: flash(error, "error") return redirect(url_for('col.col', filesystem_id=g.filesystem_id)) g.source.interaction_count += 1 filename = "{0}-{1}-reply.gpg".format(g.source.interaction_count, g.source.journalist_filename) current_app.crypto_util.encrypt( form.message.data, [ current_app.crypto_util.get_fingerprint(g.filesystem_id), config.JOURNALIST_KEY ], output=current_app.storage.path(g.filesystem_id, filename), ) try: reply = Reply(g.user, g.source, filename) db.session.add(reply) db.session.flush() seen_reply = SeenReply(reply_id=reply.id, journalist_id=g.user.id) db.session.add(seen_reply) db.session.commit() store.async_add_checksum_for_file(reply) except Exception as exc: flash( gettext("An unexpected error occurred! Please " "inform your admin."), "error") # We take a cautious approach to logging here because we're dealing # with responses to sources. It's possible the exception message # could contain information we don't want to write to disk. current_app.logger.error( "Reply from '{}' (ID {}) failed: {}!".format( g.user.username, g.user.id, exc.__class__)) else: flash( Markup("<b>{}</b> {}".format( # Translators: Precedes a message confirming the success of an operation. escape(gettext("Success!")), escape(gettext("Your reply has been stored.")))), 'success') finally: return redirect(url_for('col.col', filesystem_id=g.filesystem_id))
def add_reply(pid): #name = self.get_argument("reply[name]", default='') #email = self.get_argument("reply[email]", default='') #website = self.get_argument("reply[website]", default='') #origin_content = self.get_argument("reply[content]", default='') name = request.form["reply[name]"] email = request.form["reply[email]"] website = request.form["reply[website]"] origin_content = request.form["reply[content]"] content = markdown.markdown(formatText(origin_content)) if name == "": return redirect("/post/%d" % int(pid), error=u"请填入名字") if email == "": return redirect("/post/%d" % int(pid), error=u"请填入邮箱地址") if origin_content == "": return redirect("/post/%d" % int(pid), error=u"请输入评论内容") number = db.query(Reply).filter(Reply.pid == pid).count() + 1 db.add( Reply(pid=int(pid), name=name, email=email, website=website, content=content, origin_content=origin_content, number=number)) db.commit() base.replyerSet(name, email, website) return redirect("/post/%d" % (int(pid)))
def all_source_replies(source_uuid): if request.method == 'GET': source = get_or_404(Source, source_uuid, column=Source.uuid) return jsonify( {'replies': [reply.to_json() for reply in source.replies]}), 200 elif request.method == 'POST': source = get_or_404(Source, source_uuid, column=Source.uuid) if request.json is None: abort(400, 'please send requests in valid JSON') if 'reply' not in request.json: abort(400, 'reply not found in request body') user = get_user_object(request) data = request.json if not data['reply']: abort(400, 'reply should not be empty') source.interaction_count += 1 try: filename = current_app.storage.save_pre_encrypted_reply( source.filesystem_id, source.interaction_count, source.journalist_filename, data['reply']) except NotEncrypted: return jsonify( {'message': 'You must encrypt replies client side'}), 400 # issue #3918 filename = path.basename(filename) reply = Reply(user, source, filename) reply_uuid = data.get('uuid', None) if reply_uuid is not None: # check that is is parseable try: UUID(reply_uuid) except ValueError: abort(400, "'uuid' was not a valid UUID") reply.uuid = reply_uuid try: db.session.add(reply) db.session.add(source) db.session.commit() except IntegrityError as e: db.session.rollback() if 'UNIQUE constraint failed: replies.uuid' in str(e): abort(409, 'That UUID is already in use.') else: raise e return jsonify({'message': 'Your reply has been stored', 'uuid': reply.uuid, 'filename': reply.filename}), 201
def replyComment(): from_id = int(request.form.get("from_id")) #form to_id = int(request.form.get("to_id")) #to to_name = request.form.get("to_name") cid = int(request.form.get("cid")) content = request.form.get("content") type = request.form.get("type") if (type == '0'): rid = cid else: rid = request.form.get("rid") reply = Reply(from_id=from_id, cid=cid, content=content, to_id=to_id, to_name=to_name, reply_id=rid, type=type) try: db.session.add(reply) db.session.commit() return (jsonify({"static": 1})) except Exception as e: print(e) return (jsonify({"static": 0}))
def reply(journalist, source, num_replies): """Generates and submits *num_replies* replies to *source* from *journalist*. Returns reply objects as a list. :param Journalist journalist: The journalist to write the reply from. :param Source source: The source to send the reply to. :param int num_replies: Number of random-data replies to make. :returns: A list of the :class:`Reply`s submitted. """ assert num_replies >= 1 replies = [] for _ in range(num_replies): source.interaction_count += 1 fname = "{}-{}-reply.gpg".format(source.interaction_count, source.journalist_filename) current_app.crypto_util.encrypt( str(os.urandom(1)), [ current_app.crypto_util.get_fingerprint(source.filesystem_id), config.JOURNALIST_KEY ], current_app.storage.path(source.filesystem_id, fname)) reply = Reply(journalist, source, fname) replies.append(reply) db.session.add(reply) db.session.flush() seen_reply = SeenReply(reply_id=reply.id, journalist_id=journalist.id) db.session.add(seen_reply) db.session.commit() return replies
def save(self, user, topic, reply=None): data = self.data content = unicode(data.get('content')) data.update({ 'user_id': user.id, 'topic_id': topic.id, 'content': strip_xss_tags(content) }) if reply: category = 'edit' pre_content = reply.content cur_content = data.get('content') changed = 0 if pre_content != cur_content: diff_content = ghdiff.diff(pre_content, cur_content, css=None) changed = 1 if changed == 1: reply.content = cur_content History(user_id=user.id, content=diff_content, reply_id=reply.id).save() else: return reply else: category = 'create' reply = Reply(**data) return reply.save(category=category)
def reply(storage, journalist, source, num_replies): """Generates and submits *num_replies* replies to *source* from *journalist*. Returns reply objects as a list. :param Journalist journalist: The journalist to write the reply from. :param Source source: The source to send the reply to. :param int num_replies: Number of random-data replies to make. :returns: A list of the :class:`Reply`s submitted. """ assert num_replies >= 1 replies = [] for _ in range(num_replies): source.interaction_count += 1 fname = "{}-{}-reply.gpg".format(source.interaction_count, source.journalist_filename) EncryptionManager.get_default().encrypt_journalist_reply( for_source_with_filesystem_id=source.filesystem_id, reply_in=str(os.urandom(1)), encrypted_reply_path_out=storage.path(source.filesystem_id, fname), ) reply = Reply(journalist, source, fname, storage) replies.append(reply) db.session.add(reply) seen_reply = SeenReply(reply=reply, journalist=journalist) db.session.add(seen_reply) db.session.commit() return replies
def replies(): dict = json.loads(request.get_data(as_text=True)) comment_id = dict.get('comment_id',0) text = dict.get('text', '') re = Reply(comment_id=comment_id,user_id=current_user.id,reply=text) db.session.add(re) db.session.commit() return jsonify({'status': 'success','reply_id':re.id})
def create_source_and_submissions( source_index, source_count, num_submissions=2, num_replies=2, journalist_who_replied=None # noqa: W605, E501 ): # Store source in database codename = current_app.crypto_util.genrandomid() filesystem_id = current_app.crypto_util.hash_codename(codename) journalist_designation = current_app.crypto_util.display_id() source = Source(filesystem_id, journalist_designation) source.pending = False db.session.add(source) db.session.commit() # Generate submissions directory and generate source key os.mkdir(current_app.storage.path(source.filesystem_id)) current_app.crypto_util.genkeypair(source.filesystem_id, codename) # Generate some test submissions for _ in range(num_submissions): source.interaction_count += 1 submission_text = next(submissions) fpath = current_app.storage.save_message_submission( source.filesystem_id, source.interaction_count, source.journalist_filename, submission_text) source.last_updated = datetime.datetime.utcnow() submission = Submission(source, fpath) db.session.add(submission) # Generate some test replies for _ in range(num_replies): source.interaction_count += 1 fname = "{}-{}-reply.gpg".format(source.interaction_count, source.journalist_filename) current_app.crypto_util.encrypt( next(replies), [ current_app.crypto_util.get_fingerprint(source.filesystem_id), config.JOURNALIST_KEY ], current_app.storage.path(source.filesystem_id, fname)) if not journalist_who_replied: journalist = Journalist.query.first() else: journalist = journalist_who_replied reply = Reply(journalist, source, fname) db.session.add(reply) db.session.flush() seen_reply = SeenReply(reply_id=reply.id, journalist_id=journalist.id) db.session.add(seen_reply) db.session.commit() print("Test source {}/{} (codename: '{}', journalist designation '{}') " "added with {} submissions and {} replies".format( source_index, source_count, codename, journalist_designation, num_submissions, num_replies))
def post(self): user = user_utils.get_user_from_rosefire_user(self.user()) post = post_utils.get_post_by_id(int(self.request.get('post_id'))) reply = Reply(parent=post.key, author=user.key, text=self.request.get('text')) reply.put() time.sleep(.5) self.redirect(self.request.referer)
def post(self): cookie = self.request.cookies.get('user_id') username = utils.get_username_from_cookie(cookie) a = self.request.get("key") b = self.request.get("comment") a = a.replace(" ", "") u = Querydbase.gql("WHERE questionid = '%s'" % a).get() cor = Reply(post=a, author=username, text=b) cor.put() self.redirect('/showqu')
def create_reply(request, thread_id, main, board_name): username = request.form.get('username') split_username = username.split('#', 1) user = split_username[0] if user == '': user = '******' tripcode = split_username[1] if len(split_username) > 1 else None if tripcode: tripcode = trip(tripcode) options = request.form.get('options') subject = request.form.get('subject') content = request.form.get('content') file = request.files.get('upfile', False) filename = '' original_filename = '' thumbnail_name = '' if file and allowed_file(file.filename): original_filename = file.filename filename = ''.join( random.choice(string.ascii_uppercase + string.digits) for _ in range(10)) thumbnail_name = filename + '_thumb' filename += '.' + original_filename.rsplit('.', 1)[1].lower() thumbnail_name += '.' + original_filename.rsplit('.', 1)[1].lower() file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) thumbnail = create_thumbnail(file) thumbnail.save( os.path.join(app.config['UPLOAD_FOLDER'], thumbnail_name)) board = db.session.query(Board).filter_by(name=board_name)[0] board_id = board.post_counter + 1 board.post_counter = board_id if not main: threads = db.session.query(Thread).filter_by(board_name=board_name) thread = [ thread for thread in threads if thread.board_id == thread_id ][0] thread_id = thread.id reply = Reply( board_id=board_id, username=user, user_ip=request.remote_addr, content=content, options=options, subject=subject, tripcode=tripcode, filename=filename, thumbnail_name=thumbnail_name, original_filename=original_filename, date=datetime.now(), main=main, thread_id=thread_id, ) db.session.add(reply) db.session.commit() return reply
def new_reply(self, journalist_id, source_id): source = Source.query.get(source_id) # A source may have a null fid according to the DB, but this will # break storage.path. if source.filesystem_id is None: return journalist = Journalist.query.get(journalist_id) filename = self.fake_file(source.filesystem_id) reply = Reply(journalist, source, filename) db.session.add(reply)
def reply(obj): user = User.object.get(id=obj[u"user"]) reply = Reply(post_id=obj[u"post"], user_id=user.name, content=obj[u"content"], time=obj[u"time"]) try: reply.save() error = None except (ValueError, IntegrityError) as e: error = str(e) return error
async def test(): await orm.create_pool(loop=loop, host='127.0.0.1', port=3306, user='******', password='******', db='awesome') # u = User(name='Test2', email='*****@*****.**', passwd='1234567890', image='about:blank2') # await u.save() reply = Reply(comment_id='001527235629310f05f5b66fd3f441dac59368bc6a5c243000', user_id='0015271444248050b6689072c8f441cb53e158e535c2a9a000', user_name='abc', user_image='http://www.gravatar.com/avatar/4adcca49b3b1e5a08ac202f5d5a9e688?d=mm&s=120', content='content content content2') await reply.save()
def populate_databse(n, m): for i in range(n): post = Post(title=capitalize(get_random_title()), body=get_random_lorem(), locx=20, locy=20, username=gennames.gen_name()) db.session.add(post) for j in range(n): reply = Reply(post=post, body=get_random_lorem(), username=gennames.gen_name()) db.session.add(reply) db.session.commit()
def test_add_checksum_for_file(config, app_storage, db_model): """ Check that when we execute the `add_checksum_for_file` function, the database object is correctly updated with the actual hash of the file. We have to create our own app in order to have more control over the SQLAlchemy sessions. The fixture pushes a single app context that forces us to work within a single transaction. """ app = create_app(config) test_storage = app_storage with app.app_context(): db.create_all() source_user = create_source_user( db_session=db.session, source_passphrase=PassphraseGenerator.get_default(). generate_passphrase(), source_app_storage=test_storage, ) source = source_user.get_db_record() target_file_path = test_storage.path(source.filesystem_id, "1-foo-msg.gpg") test_message = b"hash me!" expected_hash = "f1df4a6d8659471333f7f6470d593e0911b4d487856d88c83d2d187afa195927" with open(target_file_path, "wb") as f: f.write(test_message) if db_model == Submission: db_obj = Submission(source, target_file_path, app_storage) else: journalist, _ = utils.db_helper.init_journalist() db_obj = Reply(journalist, source, target_file_path, app_storage) db.session.add(db_obj) db.session.commit() db_obj_id = db_obj.id queued_add_checksum_for_file(db_model, db_obj_id, target_file_path, app.config["SQLALCHEMY_DATABASE_URI"]) with app.app_context(): # requery to get a new object db_obj = db_model.query.filter_by(id=db_obj_id).one() assert db_obj.checksum == "sha256:" + expected_hash
def comment_reply(request, post_id, comment_id): if request.method == 'GET': reply_text = request.GET['reply_text'] # forbidden_words = Forbidden.objects.all() # # for forbidden_word in forbidden_words: # reply_text = reply_text.replace(forbidden_word.word, ("*" * len(forbidden_word.word))) comment = Comment.objects.get(pk=comment_id) reply = Reply(reply_comment=comment, reply_text=reply_text, reply_user=request.user) reply.save() return HttpResponseRedirect('/ourblog/post/' + post_id) else: return HttpResponse("Request method is not a GET")
def create_source_and_submissions(num_submissions=2, num_replies=2): # Store source in database codename = current_app.crypto_util.genrandomid() filesystem_id = current_app.crypto_util.hash_codename(codename) journalist_designation = current_app.crypto_util.display_id() source = Source(filesystem_id, journalist_designation) source.pending = False db.session.add(source) db.session.commit() # Generate submissions directory and generate source key os.mkdir(current_app.storage.path(source.filesystem_id)) current_app.crypto_util.genkeypair(source.filesystem_id, codename) # Generate some test submissions for _ in range(num_submissions): source.interaction_count += 1 fpath = current_app.storage.save_message_submission( source.filesystem_id, source.interaction_count, source.journalist_filename, 'test submission!') source.last_updated = datetime.datetime.utcnow() submission = Submission(source, fpath) db.session.add(submission) # Generate some test replies for _ in range(num_replies): source.interaction_count += 1 fname = "{}-{}-reply.gpg".format(source.interaction_count, source.journalist_filename) current_app.crypto_util.encrypt( 'this is a test reply!', [ current_app.crypto_util.getkey(source.filesystem_id), config.JOURNALIST_KEY ], current_app.storage.path(source.filesystem_id, fname)) journalist = Journalist.query.first() reply = Reply(journalist, source, fname) db.session.add(reply) db.session.commit() print("Test source (codename: '{}', journalist designation '{}') " "added with {} submissions and {} replies".format( codename, journalist_designation, num_submissions, num_replies))
def getOP(self, params, thread): op_post = self.thread_data["posts"][0] if "tim" in op_post.keys(): op_post["img_src"] = "https://i.4cdn.org/{}/{}{}".format( thread.board, op_post["tim"], op_post["ext"]) op_img_text = "{}{}".format(op_post["filename"], op_post["ext"]) if params.preserve: self.download(op_post["img_src"], op_img_text, params) op_post["img_src"] = '{}/{}'.format(thread.tid, op_img_text) if params.verbose: print("Downloading post:", op_post["no"], "posted on", op_post["now"]) op_post["board"] = thread.board p1 = Reply(op_post) self.db.insert_reply(p1) return p1
async def api_create_reply(request, *, blog_id, comment_id, content, reply_user_id): user = request.__user__ if user is None: raise APIPermissionError('请先登录哦!(Please signin first.)') if not content or not content.strip(): raise APIValueError('content') reply_user = await User.find(reply_user_id) if reply_user is None: raise APIPermissionError('您回复的账号不存在!') send_email.send_comment_email(reply_user.email, content, '/blog/%s' % blog_id) reply = Reply(comment_id=comment_id, user_id=user.id, reply_user_id=reply_user_id, user_name=user.name, user_image=user.image, content=content) await reply.save() return reply
def new_reply(self, journalist_id: int, source_id: int) -> None: source = Source.query.get(source_id) journalist = Journalist.query.get(journalist_id) source.interaction_count += 1 source.last_updated = datetime.utcnow() fname = "{}-{}-reply.gpg".format(source.interaction_count, source.journalist_filename) current_app.crypto_util.encrypt( next(replies), [ current_app.crypto_util.get_fingerprint(source.filesystem_id), sdconfig.JOURNALIST_KEY ], current_app.storage.path(source.filesystem_id, fname), ) reply = Reply(journalist, source, fname) db.session.add(reply) db.session.flush()