Add optional Discord webhook support

This commit is contained in:
rubenwardy 2021-08-17 21:16:43 +01:00
parent e5cc140d42
commit 37a7dd28d6
7 changed files with 97 additions and 6 deletions

@ -32,9 +32,10 @@ from app.rediscache import has_key, set_key
from app.tasks.importtasks import importRepoScreenshot from app.tasks.importtasks import importRepoScreenshot
from app.utils import * from app.utils import *
from . import bp, get_package_tabs from . import bp, get_package_tabs
from ...logic.LogicError import LogicError from app.logic.LogicError import LogicError
from ...logic.packages import do_edit_package from app.logic.packages import do_edit_package
from app.models.packages import PackageProvides 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' }) @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 state == PackageState.APPROVED:
if not package.approved_at: 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() package.approved_at = datetime.datetime.now()
screenshots = PackageScreenshot.query.filter_by(package=package, approved=False).all() screenshots = PackageScreenshot.query.filter_by(package=package, approved=False).all()
@ -377,6 +380,9 @@ def move_to_state(package):
s.approved = True s.approved = True
msg = "Approved {}".format(package.title) 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) 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 severity = AuditSeverity.NORMAL if current_user in package.maintainers else AuditSeverity.EDITOR

@ -23,6 +23,7 @@ from wtforms import *
from wtforms.validators import * from wtforms.validators import *
from app.models import db, PackageReview, Thread, ThreadReply, NotificationType from app.models import db, PackageReview, Thread, ThreadReply, NotificationType
from app.utils import is_package_page, addNotification, get_int_or_abort from app.utils import is_package_page, addNotification, get_int_or_abort
from app.tasks.webhooktasks import post_discord_webhook
@bp.route("/reviews/") @bp.route("/reviews/")
@ -108,6 +109,10 @@ def review(package):
addNotification(package.maintainers, current_user, type, notif_msg, addNotification(package.maintainers, current_user, type, notif_msg,
url_for("threads.view", id=thread.id), package) 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() db.session.commit()
return redirect(package.getURL("packages.view")) return redirect(package.getURL("packages.view"))

@ -15,6 +15,8 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
from flask import * from flask import *
from app.tasks.webhooktasks import post_discord_webhook
bp = Blueprint("threads", __name__) bp = Blueprint("threads", __name__)
from flask_login import current_user, login_required from flask_login import current_user, login_required
@ -243,6 +245,8 @@ def view(id):
approvers = User.query.filter(User.rank >= UserRank.APPROVER).all() approvers = User.query.filter(User.rank >= UserRank.APPROVER).all()
addNotification(approvers, current_user, NotificationType.EDITOR_MISC, msg, addNotification(approvers, current_user, NotificationType.EDITOR_MISC, msg,
thread.getViewURL(), thread.package) thread.getViewURL(), thread.package)
post_discord_webhook.delay(current_user.username,
"Replied to bot messages: {}".format(thread.getViewURL(absolute=True)), True)
db.session.commit() db.session.commit()
@ -331,7 +335,6 @@ def new():
if is_review_thread: if is_review_thread:
package.review_thread = thread package.review_thread = thread
notif_msg = "New thread '{}'".format(thread.title) notif_msg = "New thread '{}'".format(thread.title)
if package is not None: if package is not None:
addNotification(package.maintainers, current_user, NotificationType.NEW_THREAD, notif_msg, thread.getViewURL(), package) 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() approvers = User.query.filter(User.rank >= UserRank.APPROVER).all()
addNotification(approvers, current_user, NotificationType.EDITOR_MISC, notif_msg, thread.getViewURL(), package) 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() db.session.commit()
return redirect(thread.getViewURL()) return redirect(thread.getViewURL())

@ -54,7 +54,18 @@ class Thread(db.Model):
watchers = db.relationship("User", secondary=watchers, backref="watching") watchers = db.relationship("User", secondary=watchers, backref="watching")
def getViewURL(self): 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) return url_for("threads.view", id=self.id, _external=False)
def getSubscribeURL(self): def getSubscribeURL(self):

40
app/tasks/webhooktasks.py Normal file

@ -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 <https://www.gnu.org/licenses/>.
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)

@ -1,7 +1,26 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %} {% 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 %}
<meta name="og:title" content="{{ self.title() }}"/>
<meta name="og:description" content="{{ thread.get_description() }}"/>
<meta name="description" content="{{ thread.get_description() }}"/>
<meta name="og:url" content="{{ thread.getViewURL(absolute=True) }}"/>
<meta name="og:image" content="{{ thread.author.getProfilePicURL() }}"/>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

@ -28,6 +28,9 @@ MAIL_UTILS_ERROR_SEND_TO = [""]
UPLOAD_DIR = "/var/cdb/uploads/" UPLOAD_DIR = "/var/cdb/uploads/"
THUMBNAIL_DIR = "/var/cdb/thumbnails/" THUMBNAIL_DIR = "/var/cdb/thumbnails/"
DISCORD_WEBHOOK_FEED = None
DISCORD_WEBHOOK_QUEUE = None
TEMPLATES_AUTO_RELOAD = False TEMPLATES_AUTO_RELOAD = False
LOG_SQL = False LOG_SQL = False