Merge branch 'feature/webhooks' into 'develop'

Feature: Webhooks

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!53
This commit is contained in:
Aarni Halinen
2022-01-13 21:10:42 +00:00
18 changed files with 460 additions and 157 deletions
+1 -1
View File
@@ -54,7 +54,7 @@ lint:py:
needs: [] needs: []
script: script:
- pip install black==21.12b0 - pip install black==21.12b0
- black --diff . - black --check .
lint:js: lint:js:
image: node:14 image: node:14
+1 -2
View File
@@ -1,10 +1,9 @@
from django.contrib import admin from django.contrib import admin
from modeltranslation.admin import TranslationAdmin from modeltranslation.admin import TranslationAdmin
from kaehmy.models import Application, Comment, CustomRole, PresetRole, TelegramChannel from kaehmy.models import Application, Comment, CustomRole, PresetRole
admin.site.register(Application) admin.site.register(Application)
admin.site.register(Comment) admin.site.register(Comment)
admin.site.register(CustomRole) admin.site.register(CustomRole)
admin.site.register(PresetRole, TranslationAdmin) admin.site.register(PresetRole, TranslationAdmin)
admin.site.register(TelegramChannel)
@@ -0,0 +1,16 @@
# Generated by Django 2.2.26 on 2022-01-12 20:38
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("kaehmy", "0005_auto_20190312_1458"),
]
operations = [
migrations.DeleteModel(
name="TelegramChannel",
),
]
-15
View File
@@ -162,18 +162,3 @@ class Application(CommentParent):
return self.preset_roles.filter( return self.preset_roles.filter(
is_board=True is_board=True
).exists() or self.custom_roles.filter(is_board=True) ).exists() or self.custom_roles.filter(is_board=True)
# Telegram channel entry for Kaehmys
class TelegramChannel(models.Model):
"""Model containing the channel id of a Telegram chat"""
class Meta:
verbose_name = _("Telegram channel")
verbose_name_plural = _("Telegram channels")
name = models.CharField(max_length=255)
channel_id = models.CharField(max_length=255, unique=True)
def __str__(self):
return 'Telegram channel: "{}"'.format(self.name)
-36
View File
@@ -1,36 +0,0 @@
"""
A telegram bot api for whatever purposes.
TODO: kaehmy app is definitely not correct place for this
"""
import logging
import requests
from django.conf import settings
from kaehmy.models import TelegramChannel
class TelegramBot:
"""
A telegram bot api for whatever purposes
Currently only able to broadcast stuff to all registered
channels using broadcast method.
"""
def __init__(self, api_token=None):
self.api_token = api_token or settings.TELEGRAM_BOT_TOKEN
self.send_message_url = "https://api.telegram.org/bot{}/sendMessage".format(
self.api_token
)
def broadcast(self, message):
channels_ids = TelegramChannel.objects.values_list("channel_id", flat=True)
for id_ in channels_ids:
self.send_message(id_, message)
def send_message(self, channel_id, message):
"""
Send message to a chat with given channel_id
"""
data = {"chat_id": channel_id, "text": message, "parse_mode": "Markdown"}
resp = requests.post(self.send_message_url, json=data)
logging.debug(resp.content)
+3 -14
View File
@@ -13,10 +13,11 @@ from dealer.git import git
from sikweb.settings import URL from sikweb.settings import URL
from members.views.utils import * from members.views.utils import *
from kaehmy.models import Application, CustomRole, PresetRole, TelegramChannel from kaehmy.models import Application, CustomRole, PresetRole
from kaehmy.forms import ApplicationForm, CommentForm from kaehmy.forms import ApplicationForm, CommentForm
from kaehmy.tables import ExportTable from kaehmy.tables import ExportTable
from webapp.utils import send_email from webapp.utils import send_email
from webapp.models import processHooks
@ensure_csrf_cookie @ensure_csrf_cookie
@@ -144,19 +145,7 @@ def submit(request, *args, **kwargs):
send_email(to_email, subject, email_body) send_email(to_email, subject, email_body)
logging.debug(f"Sent kaehmy email to recipient <{to_email}>") logging.debug(f"Sent kaehmy email to recipient <{to_email}>")
CHAT_IDS = [channel.channel_id for channel in TelegramChannel.objects.all()] processHooks(message=f"Uusi New kaehmy! {name} -> {url}", eventType="kaehmy")
for chat_id in CHAT_IDS:
tg_string = (
"https://api.telegram.org/bot{}/sendMessage?chat_id={}&text={}".format(
settings.TELEGRAM_BOT_TOKEN,
chat_id,
"Uusi New kaehmy! {} -> {}".format(name, url),
)
)
response = requests.get(tg_string).json()
logging.debug("Telegram API response:\n{}".format(response))
logging.debug("Sent kaehmy announcement to {} channels.".format(len(CHAT_IDS)))
else: else:
context = {"error": form.errors} context = {"error": form.errors}
return render(request, "kaehmy:error.html", context) return render(request, "kaehmy:error.html", context)
Binary file not shown.
+77 -34
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-01-13 21:55+0200\n" "POT-Creation-Date: 2022-01-13 22:18+0200\n"
"PO-Revision-Date: 2017-11-02 23:09+0200\n" "PO-Revision-Date: 2017-11-02 23:09+0200\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
@@ -37,7 +37,7 @@ msgstr "Sössö articles"
msgid "Today's lunch" msgid "Today's lunch"
msgstr "" msgstr ""
#: infoscreen/models.py:212 webapp/models.py:70 #: infoscreen/models.py:212 webapp/models.py:93
msgid "Events" msgid "Events"
msgstr "Events" msgstr "Events"
@@ -113,7 +113,7 @@ msgid "Delete"
msgstr "Delete" msgstr "Delete"
#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:57 #: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:57
#: kaehmy/templates/list.html:36 webapp/models.py:144 webapp/models.py:173 #: kaehmy/templates/list.html:36 webapp/models.py:188 webapp/models.py:223
msgid "Name" msgid "Name"
msgstr "Name" msgstr "Name"
@@ -327,7 +327,7 @@ msgstr ""
msgid "Custom role name" msgid "Custom role name"
msgstr "" msgstr ""
#: kaehmy/models.py:105 webapp/models.py:174 #: kaehmy/models.py:105 webapp/models.py:224
msgid "Board member" msgid "Board member"
msgstr "Board member" msgstr "Board member"
@@ -343,14 +343,6 @@ msgstr ""
msgid "Official: {}" msgid "Official: {}"
msgstr "" msgstr ""
#: kaehmy/models.py:158
msgid "Telegram channel"
msgstr ""
#: kaehmy/models.py:159
msgid "Telegram channels"
msgstr ""
#: kaehmy/tables.py:13 #: kaehmy/tables.py:13
msgid "Roles" msgid "Roles"
msgstr "" msgstr ""
@@ -635,6 +627,10 @@ msgid "Hienoa! Jäsenhakemuksesi on nyt lähetetty."
msgstr "Amazing! Your membership application has been sent." msgstr "Amazing! Your membership application has been sent."
#: members/templates/application_success.html:9 #: members/templates/application_success.html:9
#, fuzzy
#| msgid ""
#| "Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä "
#| "admin@sahkoinsinoorikilta.fi.fi jos viestiä ei näy."
msgid "" msgid ""
"Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä " "Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä "
"admin@sahkoinsinoorikilta.fi jos viestiä ei näy." "admin@sahkoinsinoorikilta.fi jos viestiä ei näy."
@@ -1104,91 +1100,135 @@ msgstr "Go"
msgid "Aalto-yliopiston Sähköinsinöörikilta ry" msgid "Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Aalto-yliopiston Sähköinsinöörikilta ry" msgstr "Aalto-yliopiston Sähköinsinöörikilta ry"
#: webapp/models.py:18 #: webapp/models.py:20
msgid "Webapp" msgid "Webapp"
msgstr "Webapp" msgstr "Webapp"
#: webapp/models.py:26 #: webapp/models.py:28
msgid "Tag" msgid "Tag"
msgstr "Tag" msgstr "Tag"
#: webapp/models.py:27 #: webapp/models.py:29
msgid "Tags" msgid "Tags"
msgstr "Tags" msgstr "Tags"
#: webapp/models.py:34 #: webapp/models.py:36
msgid "Tag: {}" msgid "Tag: {}"
msgstr "Tag: {}" msgstr "Tag: {}"
#: webapp/models.py:52 #: webapp/models.py:54
msgid "Feed" msgid "Feed"
msgstr "" msgstr ""
#: webapp/models.py:53 #: webapp/models.py:55
msgid "Feeds" msgid "Feeds"
msgstr "" msgstr ""
#: webapp/models.py:61 webapp/models.py:80 webapp/models.py:119 #: webapp/models.py:63 webapp/models.py:102 webapp/models.py:162
#: webapp/models.py:152 webapp/models.py:198 #: webapp/models.py:196 webapp/models.py:248
msgid "Deleted: " msgid "Deleted: "
msgstr "Deleted: " msgstr "Deleted: "
#: webapp/models.py:62 #: webapp/models.py:64
msgid "{}Feed: {}" msgid "{}Feed: {}"
msgstr "" msgstr ""
#: webapp/models.py:69 #: webapp/models.py:92
msgid "Event" msgid "Event"
msgstr "" msgstr ""
#: webapp/models.py:81 #: webapp/models.py:103
msgid "{}Event: {}" msgid "{}Event: {}"
msgstr "" msgstr ""
#: webapp/models.py:90 #: webapp/models.py:133
msgid "Template question" msgid "Template question"
msgstr "" msgstr ""
#: webapp/models.py:91 #: webapp/models.py:134
msgid "Template questions" msgid "Template questions"
msgstr "" msgstr ""
#: webapp/models.py:98 #: webapp/models.py:141
msgid "Template questions: {}" msgid "Template questions: {}"
msgstr "" msgstr ""
#: webapp/models.py:105 #: webapp/models.py:148
msgid "Signup form" msgid "Signup form"
msgstr "" msgstr ""
#: webapp/models.py:106 #: webapp/models.py:149
msgid "Signup forms" msgid "Signup forms"
msgstr "" msgstr ""
#: webapp/models.py:120 #: webapp/models.py:163
msgid "#{} {}{}" msgid "#{} {}{}"
msgstr "" msgstr ""
#: webapp/models.py:138 #: webapp/models.py:181
msgid "Sign-up" msgid "Sign-up"
msgstr "" msgstr ""
#: webapp/models.py:139 #: webapp/models.py:182
msgid "Sign-ups" msgid "Sign-ups"
msgstr "" msgstr ""
#: webapp/models.py:178 #: webapp/models.py:228
msgid "board member" msgid "board member"
msgstr "board member" msgstr "board member"
#: webapp/models.py:185 #: webapp/models.py:235
msgid "JobAd" msgid "JobAd"
msgstr "" msgstr ""
#: webapp/models.py:186 #: webapp/models.py:236
msgid "JobAds" msgid "JobAds"
msgstr "" msgstr ""
#: webapp/models.py:295
#, fuzzy
#| msgid "Kaehmy"
msgid "Hook Kaehmys"
msgstr "Kaehmy"
#: webapp/models.py:296
#, fuzzy
#| msgid "Total challenges:"
msgid "Hook Ohlhafv challenges"
msgstr "Total challenges:"
#: webapp/models.py:297
msgid "Hook published news"
msgstr ""
#: webapp/models.py:298
msgid "Hook published Job Ads"
msgstr ""
#: webapp/models.py:299
msgid "Hook published events"
msgstr ""
#: webapp/models.py:300
msgid "Hook opened signups"
msgstr ""
#: webapp/models.py:325
msgid "Webhook"
msgstr ""
#: webapp/models.py:326
msgid "Webhooks"
msgstr ""
#: webapp/models.py:339
msgid "Telegram channel"
msgstr ""
#: webapp/models.py:340
msgid "Telegram channels"
msgstr ""
#: webapp/templates/contact.html:9 webapp/templates/navigation.html:20 #: webapp/templates/contact.html:9 webapp/templates/navigation.html:20
msgid "Contact" msgid "Contact"
msgstr "Contact" msgstr "Contact"
@@ -1232,3 +1272,6 @@ msgstr "Sössö"
#: webapp/templates/navigation.html:24 #: webapp/templates/navigation.html:24
msgid "Corporate" msgid "Corporate"
msgstr "Corporate" msgstr "Corporate"
#~ msgid "Hallitustyrkkypaneeli"
#~ msgstr "Panel for board applicants"
Binary file not shown.
+69 -37
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-01-13 21:55+0200\n" "POT-Creation-Date: 2022-01-13 22:18+0200\n"
"PO-Revision-Date: 2017-11-02 23:04+0200\n" "PO-Revision-Date: 2017-11-02 23:04+0200\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
@@ -38,7 +38,7 @@ msgstr "Sössön artikkelit"
msgid "Today's lunch" msgid "Today's lunch"
msgstr "Päivän lounas" msgstr "Päivän lounas"
#: infoscreen/models.py:212 webapp/models.py:70 #: infoscreen/models.py:212 webapp/models.py:93
msgid "Events" msgid "Events"
msgstr "Tapahtumat" msgstr "Tapahtumat"
@@ -114,7 +114,7 @@ msgid "Delete"
msgstr "Poista" msgstr "Poista"
#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:57 #: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:57
#: kaehmy/templates/list.html:36 webapp/models.py:144 webapp/models.py:173 #: kaehmy/templates/list.html:36 webapp/models.py:188 webapp/models.py:223
msgid "Name" msgid "Name"
msgstr "Nimi" msgstr "Nimi"
@@ -328,7 +328,7 @@ msgstr "Teksti"
msgid "Custom role name" msgid "Custom role name"
msgstr "Uusi virka" msgstr "Uusi virka"
#: kaehmy/models.py:105 webapp/models.py:174 #: kaehmy/models.py:105 webapp/models.py:224
msgid "Board member" msgid "Board member"
msgstr "Hallituksen jäsen" msgstr "Hallituksen jäsen"
@@ -344,14 +344,6 @@ msgstr "Hallitus: {}"
msgid "Official: {}" msgid "Official: {}"
msgstr "Toimari: {}" msgstr "Toimari: {}"
#: kaehmy/models.py:158
msgid "Telegram channel"
msgstr "Telegram-kanava"
#: kaehmy/models.py:159
msgid "Telegram channels"
msgstr "Telegram-kanavat"
#: kaehmy/tables.py:13 #: kaehmy/tables.py:13
msgid "Roles" msgid "Roles"
msgstr "Roolit" msgstr "Roolit"
@@ -1014,11 +1006,11 @@ msgstr "Øhlhäfv"
#: ohlhafv/models.py:22 #: ohlhafv/models.py:22
msgid "Ohlhafv challenge" msgid "Ohlhafv challenge"
msgstr "Ohlhafv haaste" msgstr "Øhlhäfv-haaste"
#: ohlhafv/models.py:23 #: ohlhafv/models.py:23
msgid "Ohlhafv challenges" msgid "Ohlhafv challenges"
msgstr "Ohlhafv haasteet" msgstr "Øhlhäfv-haasteet"
#: ohlhafv/models.py:29 #: ohlhafv/models.py:29
msgid "Team Challenge (1 x 0.33 L, 2 x 0.5 L, 1 x 1.0 L)" msgid "Team Challenge (1 x 0.33 L, 2 x 0.5 L, 1 x 1.0 L)"
@@ -1042,7 +1034,7 @@ msgstr "Sarja"
#: ohlhafv/models.py:40 #: ohlhafv/models.py:40
msgid "Ohlhafv challenge: {} vs. {}" msgid "Ohlhafv challenge: {} vs. {}"
msgstr "Ohlhafv-haaste: {} vs. {}" msgstr "Øhlhäfv-haaste: {} vs. {}"
#: ohlhafv/templates/email.html:4 #: ohlhafv/templates/email.html:4
msgid "on haastanut sinut oluenjuontimittelöön" msgid "on haastanut sinut oluenjuontimittelöön"
@@ -1093,91 +1085,131 @@ msgstr "Vaihda"
msgid "Aalto-yliopiston Sähköinsinöörikilta ry" msgid "Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Aalto-yliopiston Sähköinsinöörikilta ry" msgstr "Aalto-yliopiston Sähköinsinöörikilta ry"
#: webapp/models.py:18 #: webapp/models.py:20
msgid "Webapp" msgid "Webapp"
msgstr "Nettisivut" msgstr "Nettisivut"
#: webapp/models.py:26 #: webapp/models.py:28
msgid "Tag" msgid "Tag"
msgstr "Tunniste" msgstr "Tunniste"
#: webapp/models.py:27 #: webapp/models.py:29
msgid "Tags" msgid "Tags"
msgstr "Tunnisteet" msgstr "Tunnisteet"
#: webapp/models.py:34 #: webapp/models.py:36
msgid "Tag: {}" msgid "Tag: {}"
msgstr "Tunniste: {}" msgstr "Tunniste: {}"
#: webapp/models.py:52 #: webapp/models.py:54
msgid "Feed" msgid "Feed"
msgstr "Uutinen" msgstr "Uutinen"
#: webapp/models.py:53 #: webapp/models.py:55
msgid "Feeds" msgid "Feeds"
msgstr "Uutiset" msgstr "Uutiset"
#: webapp/models.py:61 webapp/models.py:80 webapp/models.py:119 #: webapp/models.py:63 webapp/models.py:102 webapp/models.py:162
#: webapp/models.py:152 webapp/models.py:198 #: webapp/models.py:196 webapp/models.py:248
msgid "Deleted: " msgid "Deleted: "
msgstr "Poistettu: " msgstr "Poistettu: "
#: webapp/models.py:62 #: webapp/models.py:64
msgid "{}Feed: {}" msgid "{}Feed: {}"
msgstr "{}Uutinen: {}" msgstr "{}Uutinen: {}"
#: webapp/models.py:69 #: webapp/models.py:92
msgid "Event" msgid "Event"
msgstr "Tapahtuma" msgstr "Tapahtuma"
#: webapp/models.py:81 #: webapp/models.py:103
msgid "{}Event: {}" msgid "{}Event: {}"
msgstr "{}Tapahtuma: {}" msgstr "{}Tapahtuma: {}"
#: webapp/models.py:90 #: webapp/models.py:133
msgid "Template question" msgid "Template question"
msgstr "Vakiokysymys" msgstr "Vakiokysymys"
#: webapp/models.py:91 #: webapp/models.py:134
msgid "Template questions" msgid "Template questions"
msgstr "Vakiokysymykset" msgstr "Vakiokysymykset"
#: webapp/models.py:98 #: webapp/models.py:141
msgid "Template questions: {}" msgid "Template questions: {}"
msgstr "Vakiokysymykset: {}" msgstr "Vakiokysymykset: {}"
#: webapp/models.py:105 #: webapp/models.py:148
msgid "Signup form" msgid "Signup form"
msgstr "Ilmoittautumislomake" msgstr "Ilmoittautumislomake"
#: webapp/models.py:106 #: webapp/models.py:149
msgid "Signup forms" msgid "Signup forms"
msgstr "Ilmoittautumislomakkeet" msgstr "Ilmoittautumislomakkeet"
#: webapp/models.py:120 #: webapp/models.py:163
msgid "#{} {}{}" msgid "#{} {}{}"
msgstr "" msgstr ""
#: webapp/models.py:138 #: webapp/models.py:181
msgid "Sign-up" msgid "Sign-up"
msgstr "Ilmoittautuminen" msgstr "Ilmoittautuminen"
#: webapp/models.py:139 #: webapp/models.py:182
msgid "Sign-ups" msgid "Sign-ups"
msgstr "Ilmoittautumiset" msgstr "Ilmoittautumiset"
#: webapp/models.py:178 #: webapp/models.py:228
msgid "board member" msgid "board member"
msgstr "hallituksen jäsen" msgstr "hallituksen jäsen"
#: webapp/models.py:185 #: webapp/models.py:235
msgid "JobAd" msgid "JobAd"
msgstr "Työpaikkailmoitus" msgstr "Työpaikkailmoitus"
#: webapp/models.py:186 #: webapp/models.py:236
msgid "JobAds" msgid "JobAds"
msgstr "Työpaikkailmoitukset" msgstr "Työpaikkailmoitukset"
#: webapp/models.py:295
msgid "Hook Kaehmys"
msgstr "Lähetä Kähmyt"
#: webapp/models.py:296
msgid "Hook Ohlhafv challenges"
msgstr "Lähetä Øhlhäfv-haasteet"
#: webapp/models.py:297
msgid "Hook published news"
msgstr "Lähetä julkaistut uutiset"
#: webapp/models.py:298
msgid "Hook published Job Ads"
msgstr "Lähetä työpaikkailmoitukset"
#: webapp/models.py:299
msgid "Hook published events"
msgstr "Lähetä julkaistut tapahtumat"
#: webapp/models.py:300
msgid "Hook opened signups"
msgstr "Lähetä auenneet ilmot"
#: webapp/models.py:325
msgid "Webhook"
msgstr "Webhook"
#: webapp/models.py:326
msgid "Webhooks"
msgstr "Webhookit"
#: webapp/models.py:339
msgid "Telegram channel"
msgstr "Telegram-kanava"
#: webapp/models.py:340
msgid "Telegram channels"
msgstr "Telegram-kanavat"
#: webapp/templates/contact.html:9 webapp/templates/navigation.html:20 #: webapp/templates/contact.html:9 webapp/templates/navigation.html:20
msgid "Contact" msgid "Contact"
msgstr "Yhteystiedot" msgstr "Yhteystiedot"
+5 -8
View File
@@ -1,5 +1,3 @@
"""Ohlhafv views."""
import logging import logging
from django.shortcuts import render from django.shortcuts import render
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
@@ -8,12 +6,12 @@ from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.template.loader import render_to_string from django.template.loader import render_to_string
from sikweb.settings import URL
from ohlhafv.models import OhlhafvChallenge from ohlhafv.models import OhlhafvChallenge
from ohlhafv.forms import OhlhafvForm from ohlhafv.forms import OhlhafvForm
from kaehmy.tgbot import TelegramBot
from webapp.utils import send_email from webapp.utils import send_email
from sikweb.settings import URL from webapp.models import processHooks
@require_http_methods(["GET"]) @require_http_methods(["GET"])
@@ -47,12 +45,11 @@ def ohlhafv_submit(request, *args, **kwargs):
logging.debug(f"Sent ohlhafv email to recipient <{to_email}>") logging.debug(f"Sent ohlhafv email to recipient <{to_email}>")
try: try:
tg_message = render_to_string( webhook_message = render_to_string(
"ohlhafv:tgmsg.tpl", {"challenge": challenge, "url": url} "ohlhafv:tgmsg.tpl", {"challenge": challenge, "url": url}
) )
bot = TelegramBot() processHooks(message=webhook_message, eventType="ohlhafv")
bot.broadcast(tg_message) except Exception:
except Exception: # tg spam is not critical. Ignore on failure
pass pass
else: else:
Generated
+15
View File
@@ -269,6 +269,17 @@ phonenumbers = {version = ">=7.0.2", optional = true, markers = "extra == \"phon
phonenumbers = ["phonenumbers (>=7.0.2)"] phonenumbers = ["phonenumbers (>=7.0.2)"]
phonenumberslite = ["phonenumberslite (>=7.0.2)"] phonenumberslite = ["phonenumberslite (>=7.0.2)"]
[[package]]
name = "django-polymorphic"
version = "3.1.0"
description = "Seamless polymorphic inheritance for Django models"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
Django = ">=2.1"
[[package]] [[package]]
name = "django-suit" name = "django-suit"
version = "0.2.28" version = "0.2.28"
@@ -974,6 +985,10 @@ django-phonenumber-field = [
{file = "django-phonenumber-field-4.0.0.tar.gz", hash = "sha256:d4580cc3352f4433962825f9927e6669852c1b40ec484fcb5a74064dabc1201a"}, {file = "django-phonenumber-field-4.0.0.tar.gz", hash = "sha256:d4580cc3352f4433962825f9927e6669852c1b40ec484fcb5a74064dabc1201a"},
{file = "django_phonenumber_field-4.0.0-py3-none-any.whl", hash = "sha256:2ca3bb0ada0ebc164bd903a981a34f1202a4294006e520b0da961bd7ce9f20a4"}, {file = "django_phonenumber_field-4.0.0-py3-none-any.whl", hash = "sha256:2ca3bb0ada0ebc164bd903a981a34f1202a4294006e520b0da961bd7ce9f20a4"},
] ]
django-polymorphic = [
{file = "django-polymorphic-3.1.0.tar.gz", hash = "sha256:d6955b5308bf6e41dcb22ba7c96f00b51dfa497a8a5ab1e9c06c7951bf417bf8"},
{file = "django_polymorphic-3.1.0-py3-none-any.whl", hash = "sha256:08bc4f4f4a773a19b2deced5a56deddd1ef56ebd15207bf4052e2901c25ef57e"},
]
django-suit = [ django-suit = [
{file = "django-suit-0.2.28.tar.gz", hash = "sha256:bacd8a079fcc08deb6efd0d7f60241e3c319526939ae1abe8ccfbc1b03e97104"}, {file = "django-suit-0.2.28.tar.gz", hash = "sha256:bacd8a079fcc08deb6efd0d7f60241e3c319526939ae1abe8ccfbc1b03e97104"},
{file = "django_suit-0.2.28-py2.py3-none-any.whl", hash = "sha256:256412597ac8e9461780542eebb12b37f65ff702bf23de23d07d245510c64ff2"}, {file = "django_suit-0.2.28-py2.py3-none-any.whl", hash = "sha256:256412597ac8e9461780542eebb12b37f65ff702bf23de23d07d245510c64ff2"},
+1
View File
@@ -37,6 +37,7 @@ gunicorn = "^20.1.0"
Pillow = "^8.4.0" Pillow = "^8.4.0"
sendgrid = "^6.7.0" sendgrid = "^6.7.0"
sentry-sdk = "^1.4.3" sentry-sdk = "^1.4.3"
django-polymorphic = "^3.1.0"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
coverage = "^5.5" coverage = "^5.5"
+14 -1
View File
@@ -1,7 +1,18 @@
"""File containing webapp app admin registers.""" """File containing webapp app admin registers."""
from django.contrib import admin from django.contrib import admin
from webapp.models import Feed, Tag, Event, Signup, SignupForm, TemplateQuestion, JobAd from webapp.models import (
Feed,
Tag,
Event,
Signup,
SignupForm,
TemplateQuestion,
JobAd,
BaseWebhook,
GenericWebhook,
TelegramHook,
)
from modeltranslation.admin import TranslationAdmin from modeltranslation.admin import TranslationAdmin
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
@@ -17,3 +28,5 @@ admin.site.register(SignupForm, TranslationAdmin)
admin.site.register(Signup, TranslationAdmin) admin.site.register(Signup, TranslationAdmin)
admin.site.register(TemplateQuestion, TranslationAdmin) admin.site.register(TemplateQuestion, TranslationAdmin)
admin.site.register(JobAd, TranslationAdmin) admin.site.register(JobAd, TranslationAdmin)
admin.site.register(GenericWebhook, TranslationAdmin)
admin.site.register(TelegramHook, TranslationAdmin)
@@ -0,0 +1,85 @@
# Generated by Django 2.2.26 on 2022-01-12 21:32
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("contenttypes", "0002_remove_content_type_name"),
("webapp", "0077_templatequestion_deleted"),
]
operations = [
migrations.CreateModel(
name="BaseWebhook",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=255)),
("url", models.URLField()),
("kaehmy_submit", models.BooleanField(default=False)),
("ohlhafv_submit", models.BooleanField(default=False)),
("feed_published", models.BooleanField(default=False)),
("jobad_published", models.BooleanField(default=False)),
("event_published", models.BooleanField(default=False)),
("signup_opened", models.BooleanField(default=False)),
(
"polymorphic_ctype",
models.ForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="polymorphic_webapp.basewebhook_set+",
to="contenttypes.ContentType",
),
),
],
options={
"abstract": False,
"base_manager_name": "objects",
},
),
migrations.CreateModel(
name="TelegramHook",
fields=[
(
"basewebhook_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="webapp.BaseWebhook",
),
),
("channel_id", models.CharField(max_length=255, unique=True)),
],
options={
"verbose_name": "Telegram channel",
"verbose_name_plural": "Telegram channels",
},
bases=("webapp.basewebhook",),
),
migrations.CreateModel(
name="GenericWebhook",
fields=[],
options={
"verbose_name": "Webhook",
"verbose_name_plural": "Webhooks",
"proxy": True,
"indexes": [],
"constraints": [],
},
bases=("webapp.basewebhook",),
),
]
+153 -9
View File
@@ -1,20 +1,21 @@
"""Webapp app models.""" """Webapp app models."""
from django.conf import settings
from django.db import models from django.db import models
from django.template.loader import render_to_string
from django.utils import timezone from django.utils import timezone
# from datetime import timedelta
from django.contrib.auth.models import User
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from webapp.utils import month_from_now, send_signup_email
from django.utils.translation import ugettext_lazy as _ # from datetime import timedelta
from auditlog.registry import auditlog import requests
from phonenumber_field.modelfields import PhoneNumberField
from django.contrib.postgres.fields import JSONField
from uuid import uuid4 from uuid import uuid4
import logging import logging
from django.utils.translation import ugettext_lazy as _
from django.contrib.postgres.fields import JSONField
from auditlog.registry import auditlog
from polymorphic.models import PolymorphicModel
from webapp.utils import month_from_now, send_signup_email
from sikweb.settings import FRONTEND_URL
VERBOSE_NAME = _("Webapp") VERBOSE_NAME = _("Webapp")
EMAIL_REGEX = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)" EMAIL_REGEX = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
@@ -62,6 +63,27 @@ class Feed(BaseFeed):
delete_str = _("Deleted: ") if self.deleted else "" delete_str = _("Deleted: ") if self.deleted else ""
return _("{}Feed: {}").format(delete_str, self.title) return _("{}Feed: {}").format(delete_str, self.title)
__previousVisible = False
def __init__(self, *args, **kwargs):
super(Feed, self).__init__(*args, **kwargs)
self.__previousVisible = self.visible
def save(self, force_insert=False, force_update=False, *args, **kwargs):
created = self.pk is None
super(Feed, self).save(force_insert, force_update, *args, **kwargs)
if self.visible and (created or not self.__previousVisible):
self.refresh_from_db() # Fetch so we can use primary key
url = f"https://{FRONTEND_URL}/feed/{self.pk}"
processHooks(
message=generateMessage(
"Uusi uutinen", self.title, self.description, url
),
eventType="feed",
)
self.__previousVisible = self.visible
class Event(BaseFeed): class Event(BaseFeed):
"""Model for event in guild calendar""" """Model for event in guild calendar"""
@@ -80,6 +102,27 @@ class Event(BaseFeed):
delete_str = _("Deleted: ") if self.deleted else "" delete_str = _("Deleted: ") if self.deleted else ""
return _("{}Event: {}").format(delete_str, self.title) return _("{}Event: {}").format(delete_str, self.title)
__previousVisible = False
def __init__(self, *args, **kwargs):
super(Event, self).__init__(*args, **kwargs)
self.__previousVisible = self.visible
def save(self, force_insert=False, force_update=False, *args, **kwargs):
created = self.pk is None
super(Event, self).save(force_insert, force_update, *args, **kwargs)
if self.visible and (created or not self.__previousVisible):
self.refresh_from_db() # Fetch so we can use primary key
url = f"https://{FRONTEND_URL}/events/{self.pk}"
processHooks(
message=generateMessage(
"Uusi tapahtuma", self.title, self.description, url
),
eventType="event",
)
self.__previousVisible = self.visible
class TemplateQuestion(models.Model): class TemplateQuestion(models.Model):
""" """
@@ -205,6 +248,105 @@ class JobAd(models.Model):
delete_str = _("Deleted: ") if self.deleted else "" delete_str = _("Deleted: ") if self.deleted else ""
return f"{delete_str}{self.title}" return f"{delete_str}{self.title}"
__previousVisible = False
def __init__(self, *args, **kwargs):
super(JobAd, self).__init__(*args, **kwargs)
self.__previousVisible = self.visible
def save(self, force_insert=False, force_update=False, *args, **kwargs):
created = self.pk is None
super(JobAd, self).save(force_insert, force_update, *args, **kwargs)
if self.visible and (created or not self.__previousVisible):
self.refresh_from_db() # Fetch so we can use primary key
url = f"https://{FRONTEND_URL}/jobads/{self.pk}"
processHooks(
message=generateMessage(
"Uusi työpaikkailmoitus", self.title, self.description, url
),
eventType="jobad",
)
self.__previousVisible = self.visible
def generateMessage(heading: str, title: str, description: str, url: str):
return render_to_string(
"webapp:tg_message.tpl",
{"heading": heading, "title": title, "description": description, "url": url},
)
def processHooks(message: str, eventType: str):
allHooks = BaseWebhook.objects.all()
for hook in list(allHooks):
if hook.plugs[eventType] is True:
hook.broadcast(message)
class BaseWebhook(PolymorphicModel):
"""Webhook base class instance"""
name = models.CharField(max_length=255)
url = (
models.URLField()
) # URL where webhook message is broadcast. For example Telegram webhook API
# "Plugs"; which notifications are sent to this specific webhook instance
kaehmy_submit = models.BooleanField(_("Hook Kaehmys"), default=False)
ohlhafv_submit = models.BooleanField(_("Hook Ohlhafv challenges"), default=False)
feed_published = models.BooleanField(_("Hook published news"), default=False)
jobad_published = models.BooleanField(_("Hook published Job Ads"), default=False)
event_published = models.BooleanField(_("Hook published events"), default=False)
signup_opened = models.BooleanField(_("Hook opened signups"), default=False)
@property
def plugs(self):
return {
"kaehmy": self.kaehmy_submit,
"ohlhafv": self.ohlhafv_submit,
"feed": self.feed_published,
"jobad": self.jobad_published,
"event": self.event_published,
"signup": self.signup_opened,
}
def parseData(self):
pass
def broadcast(self, message):
resp = requests.post(self.url, json=self.parseData(message))
logging.debug(f"Webhook API response: HTTP{resp.status_code}")
logging.debug(resp.content)
class GenericWebhook(BaseWebhook):
class Meta:
proxy = True
verbose_name = _("Webhook")
verbose_name_plural = _("Webhooks")
def __str__(self):
return 'Webhook "{}"'.format(self.name)
def parseData(self, message):
return {"text": message}
class TelegramHook(BaseWebhook):
"""Model containing the channel id of a Telegram chat"""
class Meta:
verbose_name = _("Telegram channel")
verbose_name_plural = _("Telegram channels")
channel_id = models.CharField(max_length=255, unique=True)
def __str__(self):
return 'Telegram channel: "{}"'.format(self.name)
def parseData(self, message):
return {"text": message, "chat_id": self.channel_id, "parse_mode": "Markdown"}
auditlog.register(Tag) auditlog.register(Tag)
auditlog.register(Feed) auditlog.register(Feed)
@@ -212,3 +354,5 @@ auditlog.register(Event)
auditlog.register(SignupForm) auditlog.register(SignupForm)
auditlog.register(Signup) auditlog.register(Signup)
auditlog.register(JobAd) auditlog.register(JobAd)
auditlog.register(GenericWebhook)
auditlog.register(TelegramHook)
+5
View File
@@ -0,0 +1,5 @@
{{ heading }}: {{ title }}
====================
{{ description }}
Lisätietoa osoitteessa: {{ url }}
+15
View File
@@ -46,3 +46,18 @@ class JobAdTranslationOptions(TranslationOptions):
"description", "description",
"content", "content",
) )
@register(BaseWebhook)
class BaseWebhookOptions(TranslationOptions):
fields = ()
@register(GenericWebhook)
class GenericWebhookOptions(TranslationOptions):
fields = ()
@register(TelegramHook)
class TelegramHookTranslationOptions(TranslationOptions):
fields = ()