mirror of https://github.com/zulip/zulip.git
integrations: Create incoming webhook for Patreon.
Creates an incoming webhook integration for Patreon. The main use case is getting notifications when new patrons sign up. Fixes #18321. Co-authored-by: Hari Prashant Bhimaraju <haripb01@gmail.com> Co-authored-by: Sudipto Mondal <sudipto.mondal1997@gmail.com>
This commit is contained in:
parent
26536a20ed
commit
1e4f5c6433
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
|
@ -441,6 +441,7 @@ WEBHOOK_INTEGRATIONS: List[WebhookIntegration] = [
|
|||
WebhookIntegration("opsgenie", ["meta-integration", "monitoring"]),
|
||||
WebhookIntegration("pagerduty", ["monitoring"], display_name="PagerDuty"),
|
||||
WebhookIntegration("papertrail", ["monitoring"]),
|
||||
WebhookIntegration("patreon", ["financial"], display_name="Patreon"),
|
||||
WebhookIntegration("pingdom", ["monitoring"]),
|
||||
WebhookIntegration("pivotal", ["project-management"], display_name="Pivotal Tracker"),
|
||||
WebhookIntegration("radarr", ["entertainment"], display_name="Radarr"),
|
||||
|
@ -777,6 +778,7 @@ DOC_SCREENSHOT_CONFIG: Dict[str, List[BaseScreenshotConfig]] = {
|
|||
"opsgenie": [ScreenshotConfig("addrecipient.json", image_name="000.png")],
|
||||
"pagerduty": [ScreenshotConfig("trigger_v2.json")],
|
||||
"papertrail": [ScreenshotConfig("short_post.json", payload_as_query_param=True)],
|
||||
"patreon": [ScreenshotConfig("members_pledge_create.json")],
|
||||
"pingdom": [ScreenshotConfig("http_up_to_down.json", image_name="002.png")],
|
||||
"pivotal": [ScreenshotConfig("v5_type_changed.json")],
|
||||
"radarr": [ScreenshotConfig("radarr_movie_grabbed.json")],
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
Get Patreon notifications in Zulip!
|
||||
|
||||
1. {!create-stream.md!}
|
||||
|
||||
1. {!create-an-incoming-webhook.md!}
|
||||
|
||||
1. {!generate-integration-url.md!}
|
||||
|
||||
You can refer to Patreons's documentation for
|
||||
[triggers](https://docs.patreon.com/#triggers-v2) and
|
||||
[webhook events](https://docs.patreon.com/#webhooks).
|
||||
|
||||
1. Login to your Patreon developer account, navigate to your Patreon Portal, and
|
||||
click on [My Webhooks](https://www.patreon.com/portal/registration/register-webhooks).
|
||||
|
||||
1. Paste the URL constructed above in the webhook URL field, and click the
|
||||
**plus** (**+**) button. Enable the events you would like to receive
|
||||
notifications for, and click **Send Test**.
|
||||
|
||||
{!congrats.md!}
|
||||
|
||||
![](/static/images/integrations/patreon/001.png)
|
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
"data": {
|
||||
"attributes": {
|
||||
"campaign_lifetime_support_cents": 0,
|
||||
"currently_entitled_amount_cents": 0,
|
||||
"email": "kopi@pangea.com",
|
||||
"full_name": "Kopi",
|
||||
"is_follower": false,
|
||||
"is_free_trial": false,
|
||||
"last_charge_date": null,
|
||||
"last_charge_status": null,
|
||||
"lifetime_support_cents": 0,
|
||||
"next_charge_date": null,
|
||||
"note": "",
|
||||
"patron_status": null,
|
||||
"pledge_cadence": null,
|
||||
"pledge_relationship_start": "2023-12-29T02:59:46.036+00:00",
|
||||
"will_pay_amount_cents": 0
|
||||
},
|
||||
"id": "38c264ba-0612-4bb9-bc33-f9755cc68bb0",
|
||||
"relationships": {
|
||||
"address": {
|
||||
"data": null
|
||||
},
|
||||
"campaign": {
|
||||
"data": {
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/campaigns/11539233"
|
||||
}
|
||||
},
|
||||
"currently_entitled_tiers": {
|
||||
"data": []
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/user/111836593"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "member"
|
||||
},
|
||||
"included": [
|
||||
{
|
||||
"attributes": {
|
||||
"created_at": "2023-12-29T01:11:16.000+00:00",
|
||||
"creation_name": "",
|
||||
"discord_server_id": null,
|
||||
"google_analytics_id": null,
|
||||
"has_rss": false,
|
||||
"has_sent_rss_notify": false,
|
||||
"image_small_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"is_charged_immediately": true,
|
||||
"is_monthly": true,
|
||||
"is_nsfw": false,
|
||||
"main_video_embed": null,
|
||||
"main_video_url": null,
|
||||
"one_liner": null,
|
||||
"patron_count": 0,
|
||||
"pay_per_name": "month",
|
||||
"pledge_url": "/checkout/11539233",
|
||||
"published_at": "2023-12-29T01:29:54.000+00:00",
|
||||
"rss_artwork_url": null,
|
||||
"rss_feed_title": null,
|
||||
"summary": null,
|
||||
"thanks_embed": null,
|
||||
"thanks_msg": null,
|
||||
"thanks_video_url": null,
|
||||
"url": "https://www.patreon.com/user?u=111828554",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"about": null,
|
||||
"created": "2023-12-29T02:38:24.000+00:00",
|
||||
"first_name": "Kopi",
|
||||
"full_name": "Kopi",
|
||||
"hide_pledges": true,
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"is_creator": false,
|
||||
"last_name": "",
|
||||
"like_count": 0,
|
||||
"thumb_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"url": "https://www.patreon.com/user?u=111836593",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"self": "https://www.patreon.com/api/oauth2/v2/members/38c264ba-0612-4bb9-bc33-f9755cc68bb0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
{
|
||||
"data": {
|
||||
"attributes": {
|
||||
"campaign_lifetime_support_cents": 0,
|
||||
"currently_entitled_amount_cents": 0,
|
||||
"email": "kopi@pangea.com",
|
||||
"full_name": "Kopi",
|
||||
"is_follower": false,
|
||||
"is_free_trial": false,
|
||||
"last_charge_date": null,
|
||||
"last_charge_status": null,
|
||||
"lifetime_support_cents": 0,
|
||||
"next_charge_date": null,
|
||||
"note": "",
|
||||
"patron_status": null,
|
||||
"pledge_cadence": null,
|
||||
"pledge_relationship_start": "2023-12-29T02:38:24.943+00:00",
|
||||
"will_pay_amount_cents": 0
|
||||
},
|
||||
"id": "38c264ba-0612-4bb9-bc33-f9755cc68bb0",
|
||||
"relationships": {
|
||||
"address": {
|
||||
"data": null
|
||||
},
|
||||
"campaign": {
|
||||
"data": {
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/campaigns/11539233"
|
||||
}
|
||||
},
|
||||
"currently_entitled_tiers": {
|
||||
"data": [
|
||||
{
|
||||
"id": "21926168",
|
||||
"type": "tier"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/user/111836593"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "member"
|
||||
},
|
||||
"included": [
|
||||
{
|
||||
"attributes": {
|
||||
"created_at": "2023-12-29T01:11:16.000+00:00",
|
||||
"creation_name": "",
|
||||
"discord_server_id": null,
|
||||
"google_analytics_id": null,
|
||||
"has_rss": false,
|
||||
"has_sent_rss_notify": false,
|
||||
"image_small_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"is_charged_immediately": true,
|
||||
"is_monthly": true,
|
||||
"is_nsfw": false,
|
||||
"main_video_embed": null,
|
||||
"main_video_url": null,
|
||||
"one_liner": null,
|
||||
"patron_count": 0,
|
||||
"pay_per_name": "month",
|
||||
"pledge_url": "/checkout/11539233",
|
||||
"published_at": "2023-12-29T01:29:54.000+00:00",
|
||||
"rss_artwork_url": null,
|
||||
"rss_feed_title": null,
|
||||
"summary": null,
|
||||
"thanks_embed": null,
|
||||
"thanks_msg": null,
|
||||
"thanks_video_url": null,
|
||||
"url": "https://www.patreon.com/user?u=111828554",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"about": null,
|
||||
"created": "2023-12-29T02:38:24.000+00:00",
|
||||
"first_name": "Kopi",
|
||||
"full_name": "Kopi",
|
||||
"hide_pledges": true,
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"is_creator": false,
|
||||
"last_name": "",
|
||||
"like_count": 0,
|
||||
"social_connections": {
|
||||
"discord": null,
|
||||
"facebook": null,
|
||||
"google": null,
|
||||
"instagram": null,
|
||||
"reddit": null,
|
||||
"spotify": null,
|
||||
"spotify_open_access": null,
|
||||
"twitch": null,
|
||||
"twitter": null,
|
||||
"vimeo": null,
|
||||
"youtube": null
|
||||
},
|
||||
"thumb_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"url": "https://www.patreon.com/user?u=111836593",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"amount_cents": 0,
|
||||
"created_at": "2023-12-29T01:11:16.871+00:00",
|
||||
"description": "",
|
||||
"discord_role_ids": null,
|
||||
"edited_at": "2023-12-29T01:11:16.871+00:00",
|
||||
"image_url": null,
|
||||
"patron_count": 0,
|
||||
"post_count": 0,
|
||||
"published": true,
|
||||
"published_at": "2023-12-29T01:11:16.871+00:00",
|
||||
"remaining": null,
|
||||
"requires_shipping": false,
|
||||
"title": "Free",
|
||||
"unpublished_at": null,
|
||||
"url": "/checkout/11539233?rid=21926168",
|
||||
"user_limit": null
|
||||
},
|
||||
"id": "21926168",
|
||||
"type": "tier"
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"self": "https://www.patreon.com/api/oauth2/v2/members/38c264ba-0612-4bb9-bc33-f9755cc68bb0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
{
|
||||
"data": {
|
||||
"attributes": {
|
||||
"campaign_lifetime_support_cents": 0,
|
||||
"currently_entitled_amount_cents": 500,
|
||||
"email": "kopi@pangea.com",
|
||||
"full_name": "Kopi",
|
||||
"is_follower": false,
|
||||
"is_free_trial": false,
|
||||
"last_charge_date": "2023-12-29T05:26:19.000+00:00",
|
||||
"last_charge_status": "Paid",
|
||||
"lifetime_support_cents": 0,
|
||||
"next_charge_date": "2024-01-29T00:00:00.000+00:00",
|
||||
"note": "",
|
||||
"patron_status": "active_patron",
|
||||
"pledge_cadence": 1,
|
||||
"pledge_relationship_start": "2023-12-29T05:26:17.634+00:00",
|
||||
"will_pay_amount_cents": 500
|
||||
},
|
||||
"id": "38c264ba-0612-4bb9-bc33-f9755cc68bb0",
|
||||
"relationships": {
|
||||
"address": {
|
||||
"data": null
|
||||
},
|
||||
"campaign": {
|
||||
"data": {
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/campaigns/11539233"
|
||||
}
|
||||
},
|
||||
"currently_entitled_tiers": {
|
||||
"data": []
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/user/111836593"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "member"
|
||||
},
|
||||
"included": [
|
||||
{
|
||||
"attributes": {
|
||||
"created_at": "2023-12-29T01:11:16.000+00:00",
|
||||
"creation_name": "",
|
||||
"discord_server_id": null,
|
||||
"google_analytics_id": null,
|
||||
"has_rss": false,
|
||||
"has_sent_rss_notify": false,
|
||||
"image_small_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"is_charged_immediately": true,
|
||||
"is_monthly": true,
|
||||
"is_nsfw": false,
|
||||
"main_video_embed": null,
|
||||
"main_video_url": null,
|
||||
"one_liner": null,
|
||||
"patron_count": 5,
|
||||
"pay_per_name": "month",
|
||||
"pledge_url": "/checkout/11539233",
|
||||
"published_at": "2023-12-29T01:29:54.000+00:00",
|
||||
"rss_artwork_url": null,
|
||||
"rss_feed_title": null,
|
||||
"summary": null,
|
||||
"thanks_embed": null,
|
||||
"thanks_msg": null,
|
||||
"thanks_video_url": null,
|
||||
"url": "https://www.patreon.com/user?u=111828554",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"about": null,
|
||||
"created": "2023-12-29T02:38:24.000+00:00",
|
||||
"first_name": "Kopi",
|
||||
"full_name": "Kopi",
|
||||
"hide_pledges": true,
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"is_creator": false,
|
||||
"last_name": "",
|
||||
"like_count": 0,
|
||||
"social_connections": {
|
||||
"discord": null,
|
||||
"facebook": null,
|
||||
"google": null,
|
||||
"instagram": null,
|
||||
"reddit": null,
|
||||
"spotify": null,
|
||||
"spotify_open_access": null,
|
||||
"twitch": null,
|
||||
"twitter": null,
|
||||
"vimeo": null,
|
||||
"youtube": null
|
||||
},
|
||||
"thumb_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"url": "https://www.patreon.com/user?u=111836593",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"self": "https://www.patreon.com/api/oauth2/v2/members/38c264ba-0612-4bb9-bc33-f9755cc68bb0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
{
|
||||
"data": {
|
||||
"attributes": {
|
||||
"campaign_lifetime_support_cents": 0,
|
||||
"currently_entitled_amount_cents": 500,
|
||||
"email": "drrosa@duck.com",
|
||||
"full_name": "David",
|
||||
"is_follower": false,
|
||||
"is_free_trial": false,
|
||||
"last_charge_date": null,
|
||||
"last_charge_status": null,
|
||||
"lifetime_support_cents": 0,
|
||||
"next_charge_date": "2024-02-01T08:00:00.000+00:00",
|
||||
"note": "",
|
||||
"patron_status": "active_patron",
|
||||
"pledge_cadence": 1,
|
||||
"pledge_relationship_start": "2024-01-10T22:37:02.731+00:00",
|
||||
"will_pay_amount_cents": 500
|
||||
},
|
||||
"id": "359a4862-bc4e-496f-8559-82a0a596ed72",
|
||||
"relationships": {
|
||||
"address": {
|
||||
"data": null
|
||||
},
|
||||
"campaign": {
|
||||
"data": {
|
||||
"id": "11579710",
|
||||
"type": "campaign"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/campaigns/11579710"
|
||||
}
|
||||
},
|
||||
"currently_entitled_tiers": {
|
||||
"data": [
|
||||
{
|
||||
"id": "21992054",
|
||||
"type": "tier"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "111828554",
|
||||
"type": "user"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/user/111828554"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "member"
|
||||
},
|
||||
"included": [
|
||||
{
|
||||
"attributes": {
|
||||
"created_at": "2024-01-06T02:15:31.000+00:00",
|
||||
"creation_name": "",
|
||||
"discord_server_id": null,
|
||||
"google_analytics_id": null,
|
||||
"has_rss": false,
|
||||
"has_sent_rss_notify": false,
|
||||
"image_small_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11579710/4d4cde3afc3e4caf900f1237f7be1347/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1707004800&token-hash=HVyUX_Jm6Qp4Zy_1uSzi2M8BOV9xkPMNwUHvDu9Mwis%3D",
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11579710/4d4cde3afc3e4caf900f1237f7be1347/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1707004800&token-hash=HVyUX_Jm6Qp4Zy_1uSzi2M8BOV9xkPMNwUHvDu9Mwis%3D",
|
||||
"is_charged_immediately": false,
|
||||
"is_monthly": false,
|
||||
"is_nsfw": false,
|
||||
"main_video_embed": null,
|
||||
"main_video_url": null,
|
||||
"one_liner": null,
|
||||
"patron_count": 0,
|
||||
"pay_per_name": "pull request",
|
||||
"pledge_url": "/checkout/Test2979",
|
||||
"published_at": "2024-01-10T22:06:53.000+00:00",
|
||||
"rss_artwork_url": null,
|
||||
"rss_feed_title": null,
|
||||
"summary": null,
|
||||
"thanks_embed": null,
|
||||
"thanks_msg": null,
|
||||
"thanks_video_url": null,
|
||||
"url": "https://www.patreon.com/Test2979",
|
||||
"vanity": "Test2979"
|
||||
},
|
||||
"id": "11579710",
|
||||
"type": "campaign"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"about": null,
|
||||
"created": "2023-12-29T01:09:55.000+00:00",
|
||||
"first_name": "David",
|
||||
"full_name": "David",
|
||||
"hide_pledges": true,
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111828554/1543482da60f49dda649625e7bdf4e15/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=8SZgxLW0nhdUhKDxEaur_X-rYnZI5Gcu-9m2lC1F18Y%3D",
|
||||
"is_creator": true,
|
||||
"last_name": "",
|
||||
"like_count": 0,
|
||||
"social_connections": {
|
||||
"discord": null,
|
||||
"facebook": null,
|
||||
"google": null,
|
||||
"instagram": null,
|
||||
"reddit": null,
|
||||
"spotify": null,
|
||||
"spotify_open_access": null,
|
||||
"twitch": null,
|
||||
"twitter": null,
|
||||
"vimeo": null,
|
||||
"youtube": null
|
||||
},
|
||||
"thumb_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111828554/1543482da60f49dda649625e7bdf4e15/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=8SZgxLW0nhdUhKDxEaur_X-rYnZI5Gcu-9m2lC1F18Y%3D",
|
||||
"url": "https://www.patreon.com/user?u=111828554",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "111828554",
|
||||
"type": "user"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"amount_cents": 500,
|
||||
"created_at": "2024-01-06T02:16:24.443+00:00",
|
||||
"description": "Access to exclusive content and more",
|
||||
"discord_role_ids": null,
|
||||
"edited_at": "2024-01-06T02:16:24.443+00:00",
|
||||
"image_url": null,
|
||||
"patron_count": 1,
|
||||
"post_count": 0,
|
||||
"published": true,
|
||||
"published_at": "2024-01-06T02:16:24.443+00:00",
|
||||
"remaining": null,
|
||||
"requires_shipping": false,
|
||||
"title": "",
|
||||
"unpublished_at": null,
|
||||
"url": "/checkout/Test2979?rid=21992054",
|
||||
"user_limit": null
|
||||
},
|
||||
"id": "21992054",
|
||||
"type": "tier"
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"self": "https://www.patreon.com/api/oauth2/v2/members/359a4862-bc4e-496f-8559-82a0a596ed72"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
{
|
||||
"data": {
|
||||
"attributes": {
|
||||
"campaign_lifetime_support_cents": 0,
|
||||
"currently_entitled_amount_cents": 0,
|
||||
"email": "kopi@pangea.com",
|
||||
"full_name": "Kopi",
|
||||
"is_follower": false,
|
||||
"is_free_trial": false,
|
||||
"last_charge_date": "2023-12-29T05:26:19.000+00:00",
|
||||
"last_charge_status": "Deleted",
|
||||
"lifetime_support_cents": 0,
|
||||
"next_charge_date": "2024-01-29T00:00:00.000+00:00",
|
||||
"note": "",
|
||||
"patron_status": "former_patron",
|
||||
"pledge_cadence": 1,
|
||||
"pledge_relationship_start": "2023-12-29T05:26:17.634+00:00",
|
||||
"will_pay_amount_cents": 0
|
||||
},
|
||||
"id": "38c264ba-0612-4bb9-bc33-f9755cc68bb0",
|
||||
"relationships": {
|
||||
"address": {
|
||||
"data": null
|
||||
},
|
||||
"campaign": {
|
||||
"data": {
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/campaigns/11539233"
|
||||
}
|
||||
},
|
||||
"currently_entitled_tiers": {
|
||||
"data": []
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/user/111836593"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "member"
|
||||
},
|
||||
"included": [
|
||||
{
|
||||
"attributes": {
|
||||
"created_at": "2023-12-29T01:11:16.000+00:00",
|
||||
"creation_name": "",
|
||||
"discord_server_id": null,
|
||||
"google_analytics_id": null,
|
||||
"has_rss": false,
|
||||
"has_sent_rss_notify": false,
|
||||
"image_small_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"is_charged_immediately": true,
|
||||
"is_monthly": true,
|
||||
"is_nsfw": false,
|
||||
"main_video_embed": null,
|
||||
"main_video_url": null,
|
||||
"one_liner": null,
|
||||
"patron_count": 4,
|
||||
"pay_per_name": "month",
|
||||
"pledge_url": "/checkout/11539233",
|
||||
"published_at": "2023-12-29T01:29:54.000+00:00",
|
||||
"rss_artwork_url": null,
|
||||
"rss_feed_title": null,
|
||||
"summary": null,
|
||||
"thanks_embed": null,
|
||||
"thanks_msg": null,
|
||||
"thanks_video_url": null,
|
||||
"url": "https://www.patreon.com/user?u=111828554",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"about": null,
|
||||
"created": "2023-12-29T02:38:24.000+00:00",
|
||||
"first_name": "Kopi",
|
||||
"full_name": "Kopi",
|
||||
"hide_pledges": true,
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"is_creator": false,
|
||||
"last_name": "",
|
||||
"like_count": 0,
|
||||
"social_connections": {
|
||||
"discord": null,
|
||||
"facebook": null,
|
||||
"google": null,
|
||||
"instagram": null,
|
||||
"reddit": null,
|
||||
"spotify": null,
|
||||
"spotify_open_access": null,
|
||||
"twitch": null,
|
||||
"twitter": null,
|
||||
"vimeo": null,
|
||||
"youtube": null
|
||||
},
|
||||
"thumb_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"url": "https://www.patreon.com/user?u=111836593",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"self": "https://www.patreon.com/api/oauth2/v2/members/38c264ba-0612-4bb9-bc33-f9755cc68bb0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
{
|
||||
"data": {
|
||||
"attributes": {
|
||||
"campaign_lifetime_support_cents": 0,
|
||||
"currently_entitled_amount_cents": 1000,
|
||||
"email": "kopi@pangea.com",
|
||||
"full_name": "Kopi",
|
||||
"is_follower": false,
|
||||
"is_free_trial": false,
|
||||
"last_charge_date": "2023-12-29T05:26:19.000+00:00",
|
||||
"last_charge_status": "Paid",
|
||||
"lifetime_support_cents": 0,
|
||||
"next_charge_date": "2024-01-29T00:00:00.000+00:00",
|
||||
"note": "",
|
||||
"patron_status": "active_patron",
|
||||
"pledge_cadence": 1,
|
||||
"pledge_relationship_start": "2023-12-29T05:26:17.634+00:00",
|
||||
"will_pay_amount_cents": 1000
|
||||
},
|
||||
"id": "38c264ba-0612-4bb9-bc33-f9755cc68bb0",
|
||||
"relationships": {
|
||||
"address": {
|
||||
"data": null
|
||||
},
|
||||
"campaign": {
|
||||
"data": {
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/campaigns/11539233"
|
||||
}
|
||||
},
|
||||
"currently_entitled_tiers": {
|
||||
"data": []
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/user/111836593"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "member"
|
||||
},
|
||||
"included": [
|
||||
{
|
||||
"attributes": {
|
||||
"created_at": "2023-12-29T01:11:16.000+00:00",
|
||||
"creation_name": "",
|
||||
"discord_server_id": null,
|
||||
"google_analytics_id": null,
|
||||
"has_rss": false,
|
||||
"has_sent_rss_notify": false,
|
||||
"image_small_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"is_charged_immediately": true,
|
||||
"is_monthly": true,
|
||||
"is_nsfw": false,
|
||||
"main_video_embed": null,
|
||||
"main_video_url": null,
|
||||
"one_liner": null,
|
||||
"patron_count": 5,
|
||||
"pay_per_name": "month",
|
||||
"pledge_url": "/checkout/11539233",
|
||||
"published_at": "2023-12-29T01:29:54.000+00:00",
|
||||
"rss_artwork_url": null,
|
||||
"rss_feed_title": null,
|
||||
"summary": null,
|
||||
"thanks_embed": null,
|
||||
"thanks_msg": null,
|
||||
"thanks_video_url": null,
|
||||
"url": "https://www.patreon.com/user?u=111828554",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"about": null,
|
||||
"created": "2023-12-29T02:38:24.000+00:00",
|
||||
"first_name": "Kopi",
|
||||
"full_name": "Kopi",
|
||||
"hide_pledges": true,
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"is_creator": false,
|
||||
"last_name": "",
|
||||
"like_count": 0,
|
||||
"social_connections": {
|
||||
"discord": null,
|
||||
"facebook": null,
|
||||
"google": null,
|
||||
"instagram": null,
|
||||
"reddit": null,
|
||||
"spotify": null,
|
||||
"spotify_open_access": null,
|
||||
"twitch": null,
|
||||
"twitter": null,
|
||||
"vimeo": null,
|
||||
"youtube": null
|
||||
},
|
||||
"thumb_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"url": "https://www.patreon.com/user?u=111836593",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"amount_cents": 0,
|
||||
"created_at": "2023-12-29T01:11:16.871+00:00",
|
||||
"description": "",
|
||||
"discord_role_ids": null,
|
||||
"edited_at": "2023-12-29T01:11:16.871+00:00",
|
||||
"image_url": null,
|
||||
"patron_count": 5,
|
||||
"post_count": 0,
|
||||
"published": true,
|
||||
"published_at": "2023-12-29T01:11:16.871+00:00",
|
||||
"remaining": null,
|
||||
"requires_shipping": false,
|
||||
"title": "Free",
|
||||
"unpublished_at": null,
|
||||
"url": "/checkout/11539233?rid=21926168",
|
||||
"user_limit": null
|
||||
},
|
||||
"id": "21926168",
|
||||
"type": "tier"
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"self": "https://www.patreon.com/api/oauth2/v2/members/38c264ba-0612-4bb9-bc33-f9755cc68bb0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
{
|
||||
"data": {
|
||||
"attributes": {
|
||||
"campaign_lifetime_support_cents": 0,
|
||||
"currently_entitled_amount_cents": 100,
|
||||
"email": "kopi@pangea.com",
|
||||
"full_name": "Kopi",
|
||||
"is_follower": false,
|
||||
"is_free_trial": true,
|
||||
"last_charge_date": null,
|
||||
"last_charge_status": null,
|
||||
"lifetime_support_cents": 0,
|
||||
"next_charge_date": null,
|
||||
"note": "",
|
||||
"patron_status": "active_patron",
|
||||
"pledge_cadence": null,
|
||||
"pledge_relationship_start": "2023-12-29T03:40:28.000+00:00",
|
||||
"will_pay_amount_cents": 0
|
||||
},
|
||||
"id": "38c264ba-0612-4bb9-bc33-f9755cc68bb0",
|
||||
"relationships": {
|
||||
"address": {
|
||||
"data": null
|
||||
},
|
||||
"campaign": {
|
||||
"data": {
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/campaigns/11539233"
|
||||
}
|
||||
},
|
||||
"currently_entitled_tiers": {
|
||||
"data": [
|
||||
{
|
||||
"id": "21926235",
|
||||
"type": "tier"
|
||||
},
|
||||
{
|
||||
"id": "21926168",
|
||||
"type": "tier"
|
||||
}
|
||||
]
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
},
|
||||
"links": {
|
||||
"related": "https://www.patreon.com/api/oauth2/v2/user/111836593"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "member"
|
||||
},
|
||||
"included": [
|
||||
{
|
||||
"attributes": {
|
||||
"created_at": "2023-12-29T01:11:16.000+00:00",
|
||||
"creation_name": "",
|
||||
"discord_server_id": null,
|
||||
"google_analytics_id": null,
|
||||
"has_rss": false,
|
||||
"has_sent_rss_notify": false,
|
||||
"image_small_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/campaign/11539233/e1a083b0bf3a44d399523f2f08e59d9c/eyJ3IjoxOTIwLCJ3ZSI6MX0%3D/1.png?token-time=1704326400&token-hash=Qb6nRUM96w1o5QgxEpHevoF7TZ5LjFVPAxpQBOLKHas%3D",
|
||||
"is_charged_immediately": true,
|
||||
"is_monthly": true,
|
||||
"is_nsfw": false,
|
||||
"main_video_embed": null,
|
||||
"main_video_url": null,
|
||||
"one_liner": null,
|
||||
"patron_count": 0,
|
||||
"pay_per_name": "month",
|
||||
"pledge_url": "/checkout/11539233",
|
||||
"published_at": "2023-12-29T01:29:54.000+00:00",
|
||||
"rss_artwork_url": null,
|
||||
"rss_feed_title": null,
|
||||
"summary": null,
|
||||
"thanks_embed": null,
|
||||
"thanks_msg": null,
|
||||
"thanks_video_url": null,
|
||||
"url": "https://www.patreon.com/user?u=111828554",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "11539233",
|
||||
"type": "campaign"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"about": null,
|
||||
"created": "2023-12-29T02:38:24.000+00:00",
|
||||
"first_name": "Kopi",
|
||||
"full_name": "Kopi",
|
||||
"hide_pledges": true,
|
||||
"image_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"is_creator": false,
|
||||
"last_name": "",
|
||||
"like_count": 0,
|
||||
"social_connections": {
|
||||
"discord": null,
|
||||
"facebook": null,
|
||||
"google": null,
|
||||
"instagram": null,
|
||||
"reddit": null,
|
||||
"spotify": null,
|
||||
"spotify_open_access": null,
|
||||
"twitch": null,
|
||||
"twitter": null,
|
||||
"vimeo": null,
|
||||
"youtube": null
|
||||
},
|
||||
"thumb_url": "https://c10.patreonusercontent.com/4/patreon-media/p/user/111836593/7e238b05a88548e2ac1afed4e0f95b74/eyJ3IjoyMDB9/1.png?token-time=2145916800&token-hash=NI0mVHbv2rxc8cQ1qGsRCOFHPbHe4Le_0L1OLKZJsYo%3D",
|
||||
"url": "https://www.patreon.com/user?u=111836593",
|
||||
"vanity": null
|
||||
},
|
||||
"id": "111836593",
|
||||
"type": "user"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"amount_cents": 100,
|
||||
"created_at": "2023-12-29T01:22:12.268+00:00",
|
||||
"description": "Access to exclusive content and more",
|
||||
"discord_role_ids": null,
|
||||
"edited_at": "2023-12-29T01:23:06.534+00:00",
|
||||
"image_url": null,
|
||||
"patron_count": 1,
|
||||
"post_count": 0,
|
||||
"published": true,
|
||||
"published_at": "2023-12-29T01:22:12.268+00:00",
|
||||
"remaining": null,
|
||||
"requires_shipping": false,
|
||||
"title": "Paid",
|
||||
"unpublished_at": null,
|
||||
"url": "/checkout/11539233?rid=21926235",
|
||||
"user_limit": null
|
||||
},
|
||||
"id": "21926235",
|
||||
"type": "tier"
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"amount_cents": 0,
|
||||
"created_at": "2023-12-29T01:11:16.871+00:00",
|
||||
"description": "",
|
||||
"discord_role_ids": null,
|
||||
"edited_at": "2023-12-29T01:11:16.871+00:00",
|
||||
"image_url": null,
|
||||
"patron_count": 0,
|
||||
"post_count": 0,
|
||||
"published": true,
|
||||
"published_at": "2023-12-29T01:11:16.871+00:00",
|
||||
"remaining": null,
|
||||
"requires_shipping": false,
|
||||
"title": "Free",
|
||||
"unpublished_at": null,
|
||||
"url": "/checkout/11539233?rid=21926168",
|
||||
"user_limit": null
|
||||
},
|
||||
"id": "21926168",
|
||||
"type": "tier"
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"self": "https://www.patreon.com/api/oauth2/v2/members/38c264ba-0612-4bb9-bc33-f9755cc68bb0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
from unittest.mock import patch
|
||||
|
||||
import orjson
|
||||
|
||||
from zerver.lib.test_classes import WebhookTestCase
|
||||
|
||||
EXPECTED_TOPIC = "membership notifications"
|
||||
IGNORED_EVENTS = [
|
||||
"pledges:create",
|
||||
"pledges:update",
|
||||
"pledges:delete",
|
||||
]
|
||||
|
||||
|
||||
class PatreonHookTests(WebhookTestCase):
|
||||
STREAM_NAME = "Patreon"
|
||||
URL_TEMPLATE = "/api/v1/external/patreon?&api_key={api_key}&stream={stream}"
|
||||
WEBHOOK_DIR_NAME = "patreon"
|
||||
|
||||
def test_patreon_members_create(self) -> None:
|
||||
expected_message = "Kopi has joined as a member!"
|
||||
self.check_webhook(
|
||||
"members_create",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
)
|
||||
|
||||
def test_patreon_members_update(self) -> None:
|
||||
expected_message = "Kopi's membership has been updated to active patron."
|
||||
self.check_webhook(
|
||||
"members_update",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
)
|
||||
|
||||
def test_patreon_members_delete(self) -> None:
|
||||
expected_message = "Kopi's membership has ended."
|
||||
self.check_webhook(
|
||||
"members_delete",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
)
|
||||
|
||||
def test_patreon_members_pledge_create(self) -> None:
|
||||
expected_message = "Kopi has pledged $5.00 per month. :tada:\nTotal number of patrons: 5"
|
||||
self.check_webhook(
|
||||
"members_pledge_create",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
)
|
||||
|
||||
def test_patreon_members_pledge_update(self) -> None:
|
||||
expected_message = "Kopi has updated their pledge to $10.00 per month. :gear:"
|
||||
self.check_webhook(
|
||||
"members_pledge_update",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
)
|
||||
|
||||
def test_patreon_members_pledge_delete(self) -> None:
|
||||
expected_message = (
|
||||
"Kopi's pledge has been cancelled. :cross_mark:\nTotal number of patrons: 4"
|
||||
)
|
||||
self.check_webhook(
|
||||
"members_pledge_delete",
|
||||
EXPECTED_TOPIC,
|
||||
expected_message,
|
||||
)
|
||||
|
||||
def test_ignored_events(self) -> None:
|
||||
# The payload for these events never gets looked at in the
|
||||
# webhook itself; it only needs to be valid JSON.
|
||||
payload = "{}"
|
||||
|
||||
for event in IGNORED_EVENTS:
|
||||
self.verify_post_is_ignored(payload, event)
|
||||
|
||||
def test_ignored_payloads(self) -> None:
|
||||
payload = orjson.loads(self.get_body("members_create"))
|
||||
payload["data"]["attributes"]["last_charge_status"] = "Declined"
|
||||
payload["data"]["attributes"]["patron_status"] = "declined_patron"
|
||||
|
||||
event_types = [
|
||||
"members:create",
|
||||
"members:update",
|
||||
"members:delete",
|
||||
"members:pledge:create",
|
||||
"members:pledge:update",
|
||||
"members:pledge:delete",
|
||||
]
|
||||
|
||||
for event in event_types:
|
||||
self.verify_post_is_ignored(orjson.dumps(payload).decode(), event)
|
||||
|
||||
def verify_post_is_ignored(self, payload: str, http_x_patreon_event: str) -> None:
|
||||
with patch("zerver.webhooks.patreon.view.check_send_webhook_message") as m:
|
||||
result = self.client_post(
|
||||
self.url,
|
||||
payload,
|
||||
HTTP_X_PATREON_EVENT=http_x_patreon_event,
|
||||
content_type="application/json",
|
||||
)
|
||||
if http_x_patreon_event in IGNORED_EVENTS:
|
||||
self.assertFalse(m.called)
|
||||
self.assert_json_success(result)
|
|
@ -0,0 +1,190 @@
|
|||
from typing import Callable, Dict, Optional
|
||||
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
|
||||
from zerver.decorator import webhook_view
|
||||
from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint
|
||||
from zerver.lib.validator import WildValue, check_int, check_none_or, check_string
|
||||
from zerver.lib.webhooks.common import (
|
||||
check_send_webhook_message,
|
||||
validate_extract_webhook_http_header,
|
||||
)
|
||||
from zerver.models import UserProfile
|
||||
|
||||
|
||||
# The events for this integration contain the ":" character, which is not appropriate in a
|
||||
# filename and requires us to deviate from the common `get_http_headers_from_filename` method
|
||||
# from zerver.lib.webhooks.common.
|
||||
def get_custom_http_headers_from_filename(http_header_key: str) -> Callable[[str], Dict[str, str]]:
|
||||
def fixture_to_headers(filename: str) -> Dict[str, str]:
|
||||
event_type = filename.replace("_", ":")
|
||||
return {http_header_key: event_type}
|
||||
|
||||
return fixture_to_headers
|
||||
|
||||
|
||||
fixture_to_headers = get_custom_http_headers_from_filename("HTTP_X_PATREON_EVENT")
|
||||
|
||||
|
||||
def get_members_create_body(payload: WildValue) -> Optional[str]:
|
||||
last_charge_status = get_last_charge_status(payload)
|
||||
patron_status = get_patron_status(payload)
|
||||
# null values indicate the member has never pledged
|
||||
if last_charge_status is None and patron_status is None:
|
||||
template = "{user_name} has joined as a member!"
|
||||
return template.format(
|
||||
user_name=get_user_name(payload),
|
||||
).rstrip()
|
||||
return None
|
||||
|
||||
|
||||
def get_members_update_body(payload: WildValue) -> Optional[str]:
|
||||
last_charge_status = get_last_charge_status(payload)
|
||||
patron_status = get_patron_status(payload)
|
||||
if last_charge_status in ("Paid", None) and patron_status in ("active_patron", "former_patron"):
|
||||
template = "{user_name}'s membership has been updated to {patron_status}."
|
||||
return template.format(
|
||||
user_name=get_user_name(payload),
|
||||
patron_status=str(patron_status).replace("_", " "),
|
||||
).rstrip()
|
||||
return None
|
||||
|
||||
|
||||
def get_members_delete_body(payload: WildValue) -> Optional[str]:
|
||||
last_charge_status = get_last_charge_status(payload)
|
||||
patron_status = get_patron_status(payload)
|
||||
# null value indicates the member has never pledged
|
||||
if last_charge_status in ("Paid", None) and patron_status != "declined_patron":
|
||||
template = "{user_name}'s membership has ended."
|
||||
return template.format(
|
||||
user_name=get_user_name(payload),
|
||||
).rstrip()
|
||||
return None
|
||||
|
||||
|
||||
def get_members_pledge_create_body(payload: WildValue) -> Optional[str]:
|
||||
last_charge_status = get_last_charge_status(payload)
|
||||
pledge_amount = get_pledge_amount(payload)
|
||||
# The only successful charge status is "Paid". null if not yet charged.
|
||||
if last_charge_status in ("Paid", None) and pledge_amount > 0:
|
||||
template = "{user_name} has pledged ${pledge_amount:.2f} per {pay_per_name}. :tada:\nTotal number of patrons: {patron_count}"
|
||||
return template.format(
|
||||
user_name=get_user_name(payload),
|
||||
pledge_amount=pledge_amount,
|
||||
pay_per_name=get_pay_per_name(payload),
|
||||
patron_count=get_patron_count(payload),
|
||||
).rstrip()
|
||||
return None
|
||||
|
||||
|
||||
def get_members_pledge_update_body(payload: WildValue) -> Optional[str]:
|
||||
last_charge_status = get_last_charge_status(payload)
|
||||
pledge_amount = get_pledge_amount(payload)
|
||||
# The only successful charge status is "Paid". null if not yet charged.
|
||||
if last_charge_status in ("Paid", None) and pledge_amount > 0:
|
||||
template = "{user_name} has updated their pledge to ${pledge_amount:.2f} per {pay_per_name}. :gear:"
|
||||
return template.format(
|
||||
user_name=get_user_name(payload),
|
||||
pledge_amount=pledge_amount,
|
||||
pay_per_name=get_pay_per_name(payload),
|
||||
).rstrip()
|
||||
return None
|
||||
|
||||
|
||||
def get_members_pledge_delete_body(payload: WildValue) -> Optional[str]:
|
||||
last_charge_status = get_last_charge_status(payload)
|
||||
if last_charge_status in ("Paid", "Deleted", None):
|
||||
template = "{user_name}'s pledge has been cancelled. :cross_mark:\nTotal number of patrons: {patron_count}"
|
||||
return template.format(
|
||||
user_name=get_user_name(payload),
|
||||
patron_count=get_patron_count(payload),
|
||||
).rstrip()
|
||||
return None
|
||||
|
||||
|
||||
def get_last_charge_status(payload: WildValue) -> Optional[str]:
|
||||
return payload["data"]["attributes"]["last_charge_status"].tame(check_none_or(check_string))
|
||||
|
||||
|
||||
def get_patron_status(payload: WildValue) -> Optional[str]:
|
||||
return payload["data"]["attributes"]["patron_status"].tame(check_none_or(check_string))
|
||||
|
||||
|
||||
def get_user_name(payload: WildValue) -> str:
|
||||
return payload["data"]["attributes"]["full_name"].tame(check_string)
|
||||
|
||||
|
||||
def get_pledge_amount(payload: WildValue) -> float:
|
||||
return payload["data"]["attributes"]["currently_entitled_amount_cents"].tame(check_int) / 100
|
||||
|
||||
|
||||
def get_patron_count(payload: WildValue) -> int:
|
||||
return payload["included"][0]["attributes"]["patron_count"].tame(check_int)
|
||||
|
||||
|
||||
def get_pay_per_name(payload: WildValue) -> str:
|
||||
return payload["included"][0]["attributes"]["pay_per_name"].tame(check_string)
|
||||
|
||||
|
||||
EVENT_FUNCTION_MAPPER: Dict[str, Callable[[WildValue], Optional[str]]] = {
|
||||
"members:create": get_members_create_body,
|
||||
"members:update": get_members_update_body,
|
||||
"members:delete": get_members_delete_body,
|
||||
"members:pledge:create": get_members_pledge_create_body,
|
||||
"members:pledge:update": get_members_pledge_update_body,
|
||||
"members:pledge:delete": get_members_pledge_delete_body,
|
||||
}
|
||||
|
||||
# deprecated events
|
||||
IGNORED_EVENTS = [
|
||||
"pledges:create",
|
||||
"pledges:update",
|
||||
"pledges:delete",
|
||||
]
|
||||
|
||||
ALL_EVENT_TYPES = list(EVENT_FUNCTION_MAPPER.keys())
|
||||
|
||||
|
||||
@webhook_view("Patreon", all_event_types=ALL_EVENT_TYPES)
|
||||
@typed_endpoint
|
||||
def api_patreon_webhook(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
*,
|
||||
payload: JsonBodyPayload[WildValue],
|
||||
) -> HttpResponse:
|
||||
header_event = validate_extract_webhook_http_header(request, "X-Patreon-Event", "Patreon")
|
||||
|
||||
event_name = get_zulip_event_name(header_event, payload)
|
||||
if event_name is None:
|
||||
# See IGNORED_EVENTS.
|
||||
return json_success(request)
|
||||
topic = "membership notifications"
|
||||
|
||||
body_function = EVENT_FUNCTION_MAPPER[event_name]
|
||||
body = body_function(payload)
|
||||
|
||||
if body is None:
|
||||
# None for payloads that are valid,
|
||||
# but where we intentionally do not send a message.
|
||||
return json_success(request)
|
||||
|
||||
check_send_webhook_message(request, user_profile, topic, body, event_name)
|
||||
return json_success(request)
|
||||
|
||||
|
||||
def get_zulip_event_name(
|
||||
header_event: str,
|
||||
payload: WildValue,
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
Usually, we return an event name that is a key in EVENT_FUNCTION_MAPPER.
|
||||
We return None for an event that we know we don't want to handle.
|
||||
"""
|
||||
if header_event in EVENT_FUNCTION_MAPPER:
|
||||
return header_event
|
||||
elif header_event in IGNORED_EVENTS:
|
||||
return None
|
||||
raise UnsupportedWebhookEventTypeError(header_event)
|
Loading…
Reference in New Issue