From 2b70e88fdaedb2cc04513b7ec5cf73217e0618d7 Mon Sep 17 00:00:00 2001 From: Lefteris Kyriazanos Date: Fri, 25 Jun 2021 01:55:44 +0300 Subject: [PATCH] integrations: Add basic open collective integration. Add basic open collective integration for the user donation event. Fixes #18319 --- .../bot_avatars/opencollective.png | Bin 0 -> 1941 bytes .../integrations/logos/opencollective.svg | Bin 0 -> 451 bytes .../integrations/opencollective/001.png | Bin 0 -> 28042 bytes zerver/lib/integrations.py | 4 ++ zerver/webhooks/opencollective/__init__.py | 0 zerver/webhooks/opencollective/doc.md | 17 +++++++ .../fixtures/one_time_donation.json | 36 ++++++++++++++ .../fixtures/one_time_incognito_donation.json | 30 +++++++++++ zerver/webhooks/opencollective/tests.py | 31 ++++++++++++ zerver/webhooks/opencollective/view.py | 47 ++++++++++++++++++ 10 files changed, 165 insertions(+) create mode 100644 static/images/integrations/bot_avatars/opencollective.png create mode 100644 static/images/integrations/logos/opencollective.svg create mode 100644 static/images/integrations/opencollective/001.png create mode 100644 zerver/webhooks/opencollective/__init__.py create mode 100644 zerver/webhooks/opencollective/doc.md create mode 100644 zerver/webhooks/opencollective/fixtures/one_time_donation.json create mode 100644 zerver/webhooks/opencollective/fixtures/one_time_incognito_donation.json create mode 100644 zerver/webhooks/opencollective/tests.py create mode 100644 zerver/webhooks/opencollective/view.py diff --git a/static/images/integrations/bot_avatars/opencollective.png b/static/images/integrations/bot_avatars/opencollective.png new file mode 100644 index 0000000000000000000000000000000000000000..839e43b315b5ee6ed85151ba228a70d384edb168 GIT binary patch literal 1941 zcmV;G2Wt30;x~*2OofFG#X8O zMKK6Oqd}rkLwx-M1`~e}jV79C)I>vU4QN%25}y!MKoop{ua34t3({7Kr8uSI4{L55 zr}MaT?>hIKbMJhUl`suiXYaMn-glq9)?Rx^^%=w2*p4Qigw2@5<1vg4co>i2AsoiN z*oQs%J^p~{p0>OLhRSZhB0LQ*nUfdI{hig$!}vLF!q0FEjwb2uE6&5m#G5?ZLph8c zxCG;c_p;(l;9a;=Z=JA|9L3l18mud@-<4w56WgD(Y*^Q|zgB8W&#G6yLv5JaHFG)z8d# zMoVthVdU|%igU&_Vwu{{Y{Xp_@BihiRrUu#E;r$$1=(~ zm1DTL(;n7{aWO5eWRLf^tMDwvr0Tq{n_kw0Cy4aacI>fVn|WVfY*XQCcL6o3WD(k-L<^EN)KHRVVHfdr}!}Z^+4cfiLIBx`$oZ<3xmlC_PpxgiphGdP%}r;Z#- z(sOb{LVDvIgY1xp#SVA3j5g#ISjl=z>d35|73OGTPFV(3EQZ!gQta4}S5a*uNl#tL zD1dgPfkzS)G1wK^TD2d$^!&Xc?+d%*gD8DK)_KUi4cwEYY^%Jvv~FZMWSZOrxj_t2jmyi2o@|J-;K!31 zcS3&A0PtElc?5ezaRh`@){-1L<2uPn4n!(_1`}4u=I%4b@QNf|w~+4;+Bk&LM_eXv zxBSNWf1E1skmsPJk`9Z&RMu2~!l9mGf>5`1ddM=x{;yAXzQY5iyc%r?g$$ zqsBC~KT_H*tw9(s+abrq+P&XtYR@UFdO_FYk8LPjtsED3I`um%O<&ViS)K`Azl_3e zWe^IOE$5VuD_mrqpIc0J>Hj(?O(#xYztlxBo--7CebFEk!JI!*X3RTF*eM@SoI_f& zQ{G9gD$*1qrRy@W#9aRq#XDt2Y$H59qtR`$Ud-pUDWu%ubg^zO+9|VQd3-HiEh_F5 z!A5+<|9@}oR+CcWEiMwq>V;EU@=ttDgh{(iG1?~Tz5jHXew6N%)gB?Z3ZE#VA#E@# za>!cORqUUW`^6^ah?o*aLLP!!XXizyjVrf*jMpQ7{>F&n|i)j)jTmt_k-ea z<;}QBq~uhp5_c6HvKX7hCG}^DvbI}9PS+`7gL8v8ynj@@`45Ol;=Q7D$lc<`hW)*6 b_-gqttgQ6tH9;1d00000NkvXXu0mjfTFlt8 literal 0 HcmV?d00001 diff --git a/static/images/integrations/logos/opencollective.svg b/static/images/integrations/logos/opencollective.svg new file mode 100644 index 0000000000000000000000000000000000000000..dcb6f06bcf900f7415bc3e50263ca2cead4efeaf GIT binary patch literal 451 zcmbu6-EP7#4256C%6;6}O@D$Y7uzkn+7px#v1z4LZATeTzY~!50+W(haXud(E8F>c zqT9J2=Ei(1%ecw1bzOPQ`7)g{bZ|1S>z>ZU#<8+N?w;kK%gN}`P)4)O{hc4%u3N9!`QLdmMHS1mr zQt5;%n}bXdG&vhM^q$3f*8ACd>-Q>Db)eKHs=`}};#iS-7T2~w6UAAh4LouQ$v_o7 zGIRB~Jxh{0VRvW|Kzb{}dGcqagi(2SaF)D~l&?6FT#kwo;z<^BBp8IkgGGrJkW>!$ QvnT(riA}cKroY+!0z8muP5=M^ literal 0 HcmV?d00001 diff --git a/static/images/integrations/opencollective/001.png b/static/images/integrations/opencollective/001.png new file mode 100644 index 0000000000000000000000000000000000000000..6805c86eb31e5a57b94092f005064fdacee3718f GIT binary patch literal 28042 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 XEbySTJO8id_Wwt^ *Webhooks*, paste the +bot URL and choose *Activity* -> *New Member*. + +{!congrats.md!} + +![](/static/images/integrations/opencollective/001.png) + +In the future, this integration can be developed in order to +support other types of *Activity* such as *New Transaction*, *Subscription Canceled* etc. diff --git a/zerver/webhooks/opencollective/fixtures/one_time_donation.json b/zerver/webhooks/opencollective/fixtures/one_time_donation.json new file mode 100644 index 0000000000..5ae5fa2fa2 --- /dev/null +++ b/zerver/webhooks/opencollective/fixtures/one_time_donation.json @@ -0,0 +1,36 @@ +{ + "createdAt": "2021-06-21T17:25:24.344Z", + "id": 1242511, + "type": "collective.member.created", + "CollectiveId": 281290, + "data": { + "member": { + "role": "BACKER", + "description": null, + "since": "2021-06-21T17:25:24.332Z", + "memberCollective": { + "id": 280138, + "type": "USER", + "slug": "leyteris-kyriazanos", + "name": "Λευτέρης Κυριαζάνος", + "company": null, + "website": null, + "twitterHandle": null, + "githubHandle": null, + "description": null, + "previewImage": null + } + }, + "order": { + "id": 183773, + "totalAmount": 100, + "currency": "EUR", + "description": "Financial contribution to Test-Webhooks", + "interval": null, + "createdAt": "2021-06-21T17:25:17.968Z", + "quantity": 1, + "formattedAmount": "€1.00", + "formattedAmountWithInterval": "€1.00" + } + } +} diff --git a/zerver/webhooks/opencollective/fixtures/one_time_incognito_donation.json b/zerver/webhooks/opencollective/fixtures/one_time_incognito_donation.json new file mode 100644 index 0000000000..db71957aac --- /dev/null +++ b/zerver/webhooks/opencollective/fixtures/one_time_incognito_donation.json @@ -0,0 +1,30 @@ +{ + "createdAt": "2021-04-30T19:20:04.346Z", + "id": 1121199, + "type": "collective.member.created", + "CollectiveId": 30312, + "data": { + "member": { + "role": "BACKER", + "description": null, + "since": "2021-04-30T19:20:04.314Z", + "memberCollective": { + "type": "USER", + "name": "Incognito", + "previewImage": null + } + }, + "order": { + "id": 168629, + "totalAmount": 100, + "currency": "USD", + "description": "Monthly financial contribution to Zulip", + "interval": "month", + "createdAt": "2021-04-30T19:19:58.647Z", + "quantity": 1, + "formattedAmount": "$1.00", + "formattedAmountWithInterval": "$1.00 / month" + } + } + } + diff --git a/zerver/webhooks/opencollective/tests.py b/zerver/webhooks/opencollective/tests.py new file mode 100644 index 0000000000..5bd9511741 --- /dev/null +++ b/zerver/webhooks/opencollective/tests.py @@ -0,0 +1,31 @@ +from zerver.lib.test_classes import WebhookTestCase + + +class OpenCollectiveHookTests(WebhookTestCase): + STREAM_NAME = "test" + URL_TEMPLATE = "/api/v1/external/opencollective?&api_key={api_key}&stream={stream}" + WEBHOOK_DIR_NAME = "opencollective" + + # Note: Include a test function per each distinct message condition your integration supports + def test_one_time_donation(self) -> None: # test one time donation + expected_topic = "New Member" + expected_message = "@_**Λευτέρης Κυριαζάνος** donated **€1.00**! :tada:" + + self.check_webhook( + "one_time_donation", + expected_topic, + expected_message, + content_type="application/x-www-form-urlencoded", + ) + + def test_one_time_incognito_donation(self) -> None: # test one time incognito donation + expected_topic = "New Member" + expected_message = "An **Incognito** member donated **$1.00**! :tada:" + + # use fixture named helloworld_hello + self.check_webhook( + "one_time_incognito_donation", + expected_topic, + expected_message, + content_type="application/x-www-form-urlencoded", + ) diff --git a/zerver/webhooks/opencollective/view.py b/zerver/webhooks/opencollective/view.py new file mode 100644 index 0000000000..08619806a3 --- /dev/null +++ b/zerver/webhooks/opencollective/view.py @@ -0,0 +1,47 @@ +from typing import Any, Dict + +from django.http import HttpRequest, HttpResponse + +from zerver.decorator import webhook_view +from zerver.lib.request import REQ, has_request_variables +from zerver.lib.response import json_success +from zerver.lib.webhooks.common import check_send_webhook_message +from zerver.models import UserProfile + +MEMBER_NAME_TEMPLATE = "{name}" +AMOUNT_TEMPLATE = "{amount}" + + +@webhook_view("OpenCollective") +@has_request_variables +def api_opencollective_webhook( + request: HttpRequest, + user_profile: UserProfile, + payload: Dict[str, Any] = REQ(argument_type="body"), +) -> HttpResponse: + + name = get_name(payload) + amount = get_amount(payload) + + # construct the body of the message + body = "" + + if name == "Incognito": # Incognito donation + body = f"An **Incognito** member donated **{amount}**! :tada:" + else: # non - Incognito donation + body = f"@_**{name}** donated **{amount}**! :tada:" + + topic = "New Member" + + # send the message + check_send_webhook_message(request, user_profile, topic, body) + + return json_success() + + +def get_name(payload: Dict[str, Any]) -> str: + return MEMBER_NAME_TEMPLATE.format(name=payload["data"]["member"]["memberCollective"]["name"]) + + +def get_amount(payload: Dict[str, Any]) -> str: + return AMOUNT_TEMPLATE.format(amount=payload["data"]["order"]["formattedAmount"])