diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b40e402..d2f196d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -54,7 +54,7 @@ lint:py: needs: [] script: - pip install black==21.12b0 - - black --diff . + - black --check . lint:js: image: node:14 diff --git a/kaehmy/admin.py b/kaehmy/admin.py index a6f11cf..f730ca5 100644 --- a/kaehmy/admin.py +++ b/kaehmy/admin.py @@ -1,10 +1,9 @@ from django.contrib import admin 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(Comment) admin.site.register(CustomRole) admin.site.register(PresetRole, TranslationAdmin) -admin.site.register(TelegramChannel) diff --git a/kaehmy/migrations/0006_delete_telegramchannel.py b/kaehmy/migrations/0006_delete_telegramchannel.py new file mode 100644 index 0000000..5f28971 --- /dev/null +++ b/kaehmy/migrations/0006_delete_telegramchannel.py @@ -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", + ), + ] diff --git a/kaehmy/models.py b/kaehmy/models.py index 4bcc09f..6582a63 100644 --- a/kaehmy/models.py +++ b/kaehmy/models.py @@ -162,18 +162,3 @@ class Application(CommentParent): return self.preset_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) diff --git a/kaehmy/tgbot.py b/kaehmy/tgbot.py deleted file mode 100644 index b3e0fdf..0000000 --- a/kaehmy/tgbot.py +++ /dev/null @@ -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) diff --git a/kaehmy/views.py b/kaehmy/views.py index 9d46404..aff149b 100644 --- a/kaehmy/views.py +++ b/kaehmy/views.py @@ -13,10 +13,11 @@ from dealer.git import git from sikweb.settings import URL 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.tables import ExportTable from webapp.utils import send_email +from webapp.models import processHooks @ensure_csrf_cookie @@ -144,19 +145,7 @@ def submit(request, *args, **kwargs): send_email(to_email, subject, email_body) logging.debug(f"Sent kaehmy email to recipient <{to_email}>") - CHAT_IDS = [channel.channel_id for channel in TelegramChannel.objects.all()] - 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))) - + processHooks(message=f"Uusi New kaehmy! {name} -> {url}", eventType="kaehmy") else: context = {"error": form.errors} return render(request, "kaehmy:error.html", context) diff --git a/locale/en/LC_MESSAGES/django.mo b/locale/en/LC_MESSAGES/django.mo index 37c486d..b725197 100644 Binary files a/locale/en/LC_MESSAGES/django.mo and b/locale/en/LC_MESSAGES/django.mo differ diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 0ec571b..d088811 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \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" "Last-Translator: \n" "Language-Team: \n" @@ -37,7 +37,7 @@ msgstr "Sössö articles" msgid "Today's lunch" msgstr "" -#: infoscreen/models.py:212 webapp/models.py:70 +#: infoscreen/models.py:212 webapp/models.py:93 msgid "Events" msgstr "Events" @@ -113,7 +113,7 @@ msgid "Delete" msgstr "Delete" #: 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" msgstr "Name" @@ -327,7 +327,7 @@ msgstr "" msgid "Custom role name" msgstr "" -#: kaehmy/models.py:105 webapp/models.py:174 +#: kaehmy/models.py:105 webapp/models.py:224 msgid "Board member" msgstr "Board member" @@ -343,14 +343,6 @@ msgstr "" msgid "Official: {}" msgstr "" -#: kaehmy/models.py:158 -msgid "Telegram channel" -msgstr "" - -#: kaehmy/models.py:159 -msgid "Telegram channels" -msgstr "" - #: kaehmy/tables.py:13 msgid "Roles" msgstr "" @@ -635,6 +627,10 @@ msgid "Hienoa! Jäsenhakemuksesi on nyt lähetetty." msgstr "Amazing! Your membership application has been sent." #: 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 "" "Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä " "admin@sahkoinsinoorikilta.fi jos viestiä ei näy." @@ -1104,91 +1100,135 @@ msgstr "Go" msgid "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" msgstr "Webapp" -#: webapp/models.py:26 +#: webapp/models.py:28 msgid "Tag" msgstr "Tag" -#: webapp/models.py:27 +#: webapp/models.py:29 msgid "Tags" msgstr "Tags" -#: webapp/models.py:34 +#: webapp/models.py:36 msgid "Tag: {}" msgstr "Tag: {}" -#: webapp/models.py:52 +#: webapp/models.py:54 msgid "Feed" msgstr "" -#: webapp/models.py:53 +#: webapp/models.py:55 msgid "Feeds" msgstr "" -#: webapp/models.py:61 webapp/models.py:80 webapp/models.py:119 -#: webapp/models.py:152 webapp/models.py:198 +#: webapp/models.py:63 webapp/models.py:102 webapp/models.py:162 +#: webapp/models.py:196 webapp/models.py:248 msgid "Deleted: " msgstr "Deleted: " -#: webapp/models.py:62 +#: webapp/models.py:64 msgid "{}Feed: {}" msgstr "" -#: webapp/models.py:69 +#: webapp/models.py:92 msgid "Event" msgstr "" -#: webapp/models.py:81 +#: webapp/models.py:103 msgid "{}Event: {}" msgstr "" -#: webapp/models.py:90 +#: webapp/models.py:133 msgid "Template question" msgstr "" -#: webapp/models.py:91 +#: webapp/models.py:134 msgid "Template questions" msgstr "" -#: webapp/models.py:98 +#: webapp/models.py:141 msgid "Template questions: {}" msgstr "" -#: webapp/models.py:105 +#: webapp/models.py:148 msgid "Signup form" msgstr "" -#: webapp/models.py:106 +#: webapp/models.py:149 msgid "Signup forms" msgstr "" -#: webapp/models.py:120 +#: webapp/models.py:163 msgid "#{} {}{}" msgstr "" -#: webapp/models.py:138 +#: webapp/models.py:181 msgid "Sign-up" msgstr "" -#: webapp/models.py:139 +#: webapp/models.py:182 msgid "Sign-ups" msgstr "" -#: webapp/models.py:178 +#: webapp/models.py:228 msgid "board member" msgstr "board member" -#: webapp/models.py:185 +#: webapp/models.py:235 msgid "JobAd" msgstr "" -#: webapp/models.py:186 +#: webapp/models.py:236 msgid "JobAds" 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 msgid "Contact" msgstr "Contact" @@ -1232,3 +1272,6 @@ msgstr "Sössö" #: webapp/templates/navigation.html:24 msgid "Corporate" msgstr "Corporate" + +#~ msgid "Hallitustyrkkypaneeli" +#~ msgstr "Panel for board applicants" diff --git a/locale/fi/LC_MESSAGES/django.mo b/locale/fi/LC_MESSAGES/django.mo index 8b2d5e6..e7d259a 100644 Binary files a/locale/fi/LC_MESSAGES/django.mo and b/locale/fi/LC_MESSAGES/django.mo differ diff --git a/locale/fi/LC_MESSAGES/django.po b/locale/fi/LC_MESSAGES/django.po index 8d7d45c..81fc74a 100644 --- a/locale/fi/LC_MESSAGES/django.po +++ b/locale/fi/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \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" "Last-Translator: \n" "Language-Team: \n" @@ -38,7 +38,7 @@ msgstr "Sössön artikkelit" msgid "Today's lunch" msgstr "Päivän lounas" -#: infoscreen/models.py:212 webapp/models.py:70 +#: infoscreen/models.py:212 webapp/models.py:93 msgid "Events" msgstr "Tapahtumat" @@ -114,7 +114,7 @@ msgid "Delete" msgstr "Poista" #: 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" msgstr "Nimi" @@ -328,7 +328,7 @@ msgstr "Teksti" msgid "Custom role name" msgstr "Uusi virka" -#: kaehmy/models.py:105 webapp/models.py:174 +#: kaehmy/models.py:105 webapp/models.py:224 msgid "Board member" msgstr "Hallituksen jäsen" @@ -344,14 +344,6 @@ msgstr "Hallitus: {}" msgid "Official: {}" 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 msgid "Roles" msgstr "Roolit" @@ -1014,11 +1006,11 @@ msgstr "Øhlhäfv" #: ohlhafv/models.py:22 msgid "Ohlhafv challenge" -msgstr "Ohlhafv haaste" +msgstr "Øhlhäfv-haaste" #: ohlhafv/models.py:23 msgid "Ohlhafv challenges" -msgstr "Ohlhafv haasteet" +msgstr "Øhlhäfv-haasteet" #: ohlhafv/models.py:29 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 msgid "Ohlhafv challenge: {} vs. {}" -msgstr "Ohlhafv-haaste: {} vs. {}" +msgstr "Øhlhäfv-haaste: {} vs. {}" #: ohlhafv/templates/email.html:4 msgid "on haastanut sinut oluenjuontimittelöön" @@ -1093,91 +1085,131 @@ msgstr "Vaihda" msgid "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" msgstr "Nettisivut" -#: webapp/models.py:26 +#: webapp/models.py:28 msgid "Tag" msgstr "Tunniste" -#: webapp/models.py:27 +#: webapp/models.py:29 msgid "Tags" msgstr "Tunnisteet" -#: webapp/models.py:34 +#: webapp/models.py:36 msgid "Tag: {}" msgstr "Tunniste: {}" -#: webapp/models.py:52 +#: webapp/models.py:54 msgid "Feed" msgstr "Uutinen" -#: webapp/models.py:53 +#: webapp/models.py:55 msgid "Feeds" msgstr "Uutiset" -#: webapp/models.py:61 webapp/models.py:80 webapp/models.py:119 -#: webapp/models.py:152 webapp/models.py:198 +#: webapp/models.py:63 webapp/models.py:102 webapp/models.py:162 +#: webapp/models.py:196 webapp/models.py:248 msgid "Deleted: " msgstr "Poistettu: " -#: webapp/models.py:62 +#: webapp/models.py:64 msgid "{}Feed: {}" msgstr "{}Uutinen: {}" -#: webapp/models.py:69 +#: webapp/models.py:92 msgid "Event" msgstr "Tapahtuma" -#: webapp/models.py:81 +#: webapp/models.py:103 msgid "{}Event: {}" msgstr "{}Tapahtuma: {}" -#: webapp/models.py:90 +#: webapp/models.py:133 msgid "Template question" msgstr "Vakiokysymys" -#: webapp/models.py:91 +#: webapp/models.py:134 msgid "Template questions" msgstr "Vakiokysymykset" -#: webapp/models.py:98 +#: webapp/models.py:141 msgid "Template questions: {}" msgstr "Vakiokysymykset: {}" -#: webapp/models.py:105 +#: webapp/models.py:148 msgid "Signup form" msgstr "Ilmoittautumislomake" -#: webapp/models.py:106 +#: webapp/models.py:149 msgid "Signup forms" msgstr "Ilmoittautumislomakkeet" -#: webapp/models.py:120 +#: webapp/models.py:163 msgid "#{} {}{}" msgstr "" -#: webapp/models.py:138 +#: webapp/models.py:181 msgid "Sign-up" msgstr "Ilmoittautuminen" -#: webapp/models.py:139 +#: webapp/models.py:182 msgid "Sign-ups" msgstr "Ilmoittautumiset" -#: webapp/models.py:178 +#: webapp/models.py:228 msgid "board member" msgstr "hallituksen jäsen" -#: webapp/models.py:185 +#: webapp/models.py:235 msgid "JobAd" msgstr "Työpaikkailmoitus" -#: webapp/models.py:186 +#: webapp/models.py:236 msgid "JobAds" 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 msgid "Contact" msgstr "Yhteystiedot" diff --git a/ohlhafv/views.py b/ohlhafv/views.py index 81fcb3b..f9d3c3b 100644 --- a/ohlhafv/views.py +++ b/ohlhafv/views.py @@ -1,5 +1,3 @@ -"""Ohlhafv views.""" - import logging from django.shortcuts import render 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.template.loader import render_to_string +from sikweb.settings import URL from ohlhafv.models import OhlhafvChallenge from ohlhafv.forms import OhlhafvForm -from kaehmy.tgbot import TelegramBot from webapp.utils import send_email -from sikweb.settings import URL +from webapp.models import processHooks @require_http_methods(["GET"]) @@ -47,12 +45,11 @@ def ohlhafv_submit(request, *args, **kwargs): logging.debug(f"Sent ohlhafv email to recipient <{to_email}>") try: - tg_message = render_to_string( + webhook_message = render_to_string( "ohlhafv:tgmsg.tpl", {"challenge": challenge, "url": url} ) - bot = TelegramBot() - bot.broadcast(tg_message) - except Exception: # tg spam is not critical. Ignore on failure + processHooks(message=webhook_message, eventType="ohlhafv") + except Exception: pass else: diff --git a/poetry.lock b/poetry.lock index 2ae1c3d..8b64ce4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -269,6 +269,17 @@ phonenumbers = {version = ">=7.0.2", optional = true, markers = "extra == \"phon phonenumbers = ["phonenumbers (>=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]] name = "django-suit" 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-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 = [ {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"}, diff --git a/pyproject.toml b/pyproject.toml index 292952f..d6e9612 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ gunicorn = "^20.1.0" Pillow = "^8.4.0" sendgrid = "^6.7.0" sentry-sdk = "^1.4.3" +django-polymorphic = "^3.1.0" [tool.poetry.dev-dependencies] coverage = "^5.5" diff --git a/webapp/admin.py b/webapp/admin.py index 0913d34..eb4b569 100644 --- a/webapp/admin.py +++ b/webapp/admin.py @@ -1,7 +1,18 @@ """File containing webapp app admin registers.""" 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 django.contrib.auth.models import Permission @@ -17,3 +28,5 @@ admin.site.register(SignupForm, TranslationAdmin) admin.site.register(Signup, TranslationAdmin) admin.site.register(TemplateQuestion, TranslationAdmin) admin.site.register(JobAd, TranslationAdmin) +admin.site.register(GenericWebhook, TranslationAdmin) +admin.site.register(TelegramHook, TranslationAdmin) diff --git a/webapp/migrations/0078_basewebhook_genericwebhook_telegramhook.py b/webapp/migrations/0078_basewebhook_genericwebhook_telegramhook.py new file mode 100644 index 0000000..07e1c1a --- /dev/null +++ b/webapp/migrations/0078_basewebhook_genericwebhook_telegramhook.py @@ -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",), + ), + ] diff --git a/webapp/models.py b/webapp/models.py index e874553..5d077c5 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -1,20 +1,21 @@ """Webapp app models.""" -from django.conf import settings from django.db import models +from django.template.loader import render_to_string 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.dispatch import receiver -from webapp.utils import month_from_now, send_signup_email -from django.utils.translation import ugettext_lazy as _ -from auditlog.registry import auditlog -from phonenumber_field.modelfields import PhoneNumberField -from django.contrib.postgres.fields import JSONField + +# from datetime import timedelta +import requests from uuid import uuid4 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") 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 "" 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): """Model for event in guild calendar""" @@ -80,6 +102,27 @@ class Event(BaseFeed): delete_str = _("Deleted: ") if self.deleted else "" 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): """ @@ -205,6 +248,105 @@ class JobAd(models.Model): delete_str = _("Deleted: ") if self.deleted else "" 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(Feed) @@ -212,3 +354,5 @@ auditlog.register(Event) auditlog.register(SignupForm) auditlog.register(Signup) auditlog.register(JobAd) +auditlog.register(GenericWebhook) +auditlog.register(TelegramHook) diff --git a/webapp/templates/tg_message.tpl b/webapp/templates/tg_message.tpl new file mode 100644 index 0000000..bab3c8f --- /dev/null +++ b/webapp/templates/tg_message.tpl @@ -0,0 +1,5 @@ +{{ heading }}: {{ title }} +==================== +{{ description }} + +Lisätietoa osoitteessa: {{ url }} \ No newline at end of file diff --git a/webapp/translation.py b/webapp/translation.py index 4da8cd6..df4b799 100644 --- a/webapp/translation.py +++ b/webapp/translation.py @@ -46,3 +46,18 @@ class JobAdTranslationOptions(TranslationOptions): "description", "content", ) + + +@register(BaseWebhook) +class BaseWebhookOptions(TranslationOptions): + fields = () + + +@register(GenericWebhook) +class GenericWebhookOptions(TranslationOptions): + fields = () + + +@register(TelegramHook) +class TelegramHookTranslationOptions(TranslationOptions): + fields = ()