From 37a7dd28d6cc9afce389fa5fac51d2eb5b23e81b Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Tue, 17 Aug 2021 21:16:43 +0100 Subject: [PATCH] Add optional Discord webhook support --- app/blueprints/packages/packages.py | 10 ++++++-- app/blueprints/packages/reviews.py | 5 ++++ app/blueprints/threads/__init__.py | 9 ++++++- app/models/threads.py | 15 +++++++++-- app/tasks/webhooktasks.py | 40 +++++++++++++++++++++++++++++ app/templates/threads/view.html | 21 ++++++++++++++- config.example.cfg | 3 +++ 7 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 app/tasks/webhooktasks.py diff --git a/app/blueprints/packages/packages.py b/app/blueprints/packages/packages.py index 5d8711af..2dd0340b 100644 --- a/app/blueprints/packages/packages.py +++ b/app/blueprints/packages/packages.py @@ -32,9 +32,10 @@ from app.rediscache import has_key, set_key from app.tasks.importtasks import importRepoScreenshot from app.utils import * from . import bp, get_package_tabs -from ...logic.LogicError import LogicError -from ...logic.packages import do_edit_package +from app.logic.LogicError import LogicError +from app.logic.packages import do_edit_package from app.models.packages import PackageProvides +from app.tasks.webhooktasks import post_discord_webhook @menu.register_menu(bp, ".mods", "Mods", order=11, endpoint_arguments_constructor=lambda: { 'type': 'mod' }) @@ -370,6 +371,8 @@ def move_to_state(package): if state == PackageState.APPROVED: if not package.approved_at: + post_discord_webhook.delay(package.author.username, + "New package {}".format(package.getURL("packages.view", absolute=True)), False) package.approved_at = datetime.datetime.now() screenshots = PackageScreenshot.query.filter_by(package=package, approved=False).all() @@ -377,6 +380,9 @@ def move_to_state(package): s.approved = True msg = "Approved {}".format(package.title) + elif state == PackageState.READY_FOR_REVIEW: + post_discord_webhook.delay(package.author.username, + "Ready for Review: {}".format(package.getURL("packages.view", absolute=True)), True) addNotification(package.maintainers, current_user, NotificationType.PACKAGE_APPROVAL, msg, package.getURL("packages.view"), package) severity = AuditSeverity.NORMAL if current_user in package.maintainers else AuditSeverity.EDITOR diff --git a/app/blueprints/packages/reviews.py b/app/blueprints/packages/reviews.py index 4523a7f4..0d8c5e0c 100644 --- a/app/blueprints/packages/reviews.py +++ b/app/blueprints/packages/reviews.py @@ -23,6 +23,7 @@ from wtforms import * from wtforms.validators import * from app.models import db, PackageReview, Thread, ThreadReply, NotificationType from app.utils import is_package_page, addNotification, get_int_or_abort +from app.tasks.webhooktasks import post_discord_webhook @bp.route("/reviews/") @@ -108,6 +109,10 @@ def review(package): addNotification(package.maintainers, current_user, type, notif_msg, url_for("threads.view", id=thread.id), package) + if was_new: + post_discord_webhook.delay(thread.author.username, + "Reviewed {}: {}".format(package.title, thread.getViewURL(absolute=True)), False) + db.session.commit() return redirect(package.getURL("packages.view")) diff --git a/app/blueprints/threads/__init__.py b/app/blueprints/threads/__init__.py index 710d4355..7afbdd5a 100644 --- a/app/blueprints/threads/__init__.py +++ b/app/blueprints/threads/__init__.py @@ -15,6 +15,8 @@ # along with this program. If not, see . from flask import * +from app.tasks.webhooktasks import post_discord_webhook + bp = Blueprint("threads", __name__) from flask_login import current_user, login_required @@ -243,6 +245,8 @@ def view(id): approvers = User.query.filter(User.rank >= UserRank.APPROVER).all() addNotification(approvers, current_user, NotificationType.EDITOR_MISC, msg, thread.getViewURL(), thread.package) + post_discord_webhook.delay(current_user.username, + "Replied to bot messages: {}".format(thread.getViewURL(absolute=True)), True) db.session.commit() @@ -331,7 +335,6 @@ def new(): if is_review_thread: package.review_thread = thread - notif_msg = "New thread '{}'".format(thread.title) if package is not None: addNotification(package.maintainers, current_user, NotificationType.NEW_THREAD, notif_msg, thread.getViewURL(), package) @@ -339,6 +342,10 @@ def new(): approvers = User.query.filter(User.rank >= UserRank.APPROVER).all() addNotification(approvers, current_user, NotificationType.EDITOR_MISC, notif_msg, thread.getViewURL(), package) + if is_review_thread: + post_discord_webhook.delay(current_user.username, + "Opened approval thread: {}".format(thread.getViewURL(absolute=True)), True) + db.session.commit() return redirect(thread.getViewURL()) diff --git a/app/models/threads.py b/app/models/threads.py index f002942e..13dae756 100644 --- a/app/models/threads.py +++ b/app/models/threads.py @@ -54,8 +54,19 @@ class Thread(db.Model): watchers = db.relationship("User", secondary=watchers, backref="watching") - def getViewURL(self): - return url_for("threads.view", id=self.id, _external=False) + def get_description(self): + comment = self.replies[0].comment.replace("\r\n", " ").replace("\n", " ").replace(" ", " ") + if len(comment) > 100: + return comment[:97] + "..." + else: + return comment + + def getViewURL(self, absolute=False): + if absolute: + from ..utils import abs_url_for + return abs_url_for("threads.view", id=self.id) + else: + return url_for("threads.view", id=self.id, _external=False) def getSubscribeURL(self): return url_for("threads.subscribe", id=self.id) diff --git a/app/tasks/webhooktasks.py b/app/tasks/webhooktasks.py new file mode 100644 index 00000000..0abcf72a --- /dev/null +++ b/app/tasks/webhooktasks.py @@ -0,0 +1,40 @@ +# ContentDB +# Copyright (C) 2021 rubenwardy +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +from typing import Optional + +import requests + +from app import app +from app.models import User +from app.tasks import celery + +@celery.task() +def post_discord_webhook(username: Optional[str], content: str, is_queue: bool): + discord_url = app.config.get("DISCORD_WEBHOOK_QUEUE" if is_queue else "DISCORD_WEBHOOK_FEED") + if discord_url is None: + return + + json = { + "content": content, + } + + if username: + json["username"] = username + user = User.query.filter_by(username=username).first() + if user: + json["avatar_url"] = user.getProfilePicURL().replace("/./", "/") + + requests.post(discord_url, json=json) diff --git a/app/templates/threads/view.html b/app/templates/threads/view.html index 23c32327..bff5c332 100644 --- a/app/templates/threads/view.html +++ b/app/templates/threads/view.html @@ -1,7 +1,26 @@ {% extends "base.html" %} {% block title %} -{{ thread.title }} - {{ _("Threads") }} + {%- if thread.package -%} + {%- if thread.review -%} + {%- if thread.review.recommends -%} + {%- set rating = "👍" -%} + {%- else -%} + {%- set rating = "👎" -%} + {%- endif -%} + {%- endif -%} + {{ rating }} {{ _("%(title)s on %(package)s", title=thread.title, package=thread.package.title) }} + {%- else -%} + {{ thread.title }} + {%- endif -%} +{% endblock %} + +{% block headextra %} + + + + + {% endblock %} {% block content %} diff --git a/config.example.cfg b/config.example.cfg index d933832d..fa974eca 100644 --- a/config.example.cfg +++ b/config.example.cfg @@ -28,6 +28,9 @@ MAIL_UTILS_ERROR_SEND_TO = [""] UPLOAD_DIR = "/var/cdb/uploads/" THUMBNAIL_DIR = "/var/cdb/thumbnails/" +DISCORD_WEBHOOK_FEED = None +DISCORD_WEBHOOK_QUEUE = None + TEMPLATES_AUTO_RELOAD = False LOG_SQL = False