200 lines
4.4 KiB
TypeScript
200 lines
4.4 KiB
TypeScript
|
import crypto from 'crypto'
|
||
|
import * as qs from 'querystring'
|
||
|
import fetch from 'node-fetch'
|
||
|
import {
|
||
|
_handler
|
||
|
} from '../_handler'
|
||
|
import {
|
||
|
BadRequest
|
||
|
} from '../errors'
|
||
|
import {
|
||
|
findUser
|
||
|
} from '../storage'
|
||
|
|
||
|
function signRequestBody(key: string, body: string) {
|
||
|
return `sha1=${crypto
|
||
|
.createHmac('sha1', key)
|
||
|
.update(body, 'utf8')
|
||
|
.digest('hex')}`
|
||
|
}
|
||
|
|
||
|
function replaceTemplate(string: string, payload: WebhookPayloadSponsorship) {
|
||
|
return string
|
||
|
.replace(/GITHUB_USERNAME/g, payload.sponsorship.sponsor.login)
|
||
|
.replace(/TIER_PRICE/g, payload.sponsorship.tier.monthly_price_in_dollars.toString())
|
||
|
}
|
||
|
|
||
|
type GitHubUser = {
|
||
|
login: string
|
||
|
id: number
|
||
|
node_id: string
|
||
|
avatar_url: string
|
||
|
gravatar_id: string
|
||
|
url: string
|
||
|
html_url: string
|
||
|
followers_url: string
|
||
|
following_url: string
|
||
|
gists_url: string
|
||
|
starred_url: string
|
||
|
subscriptions_url: string
|
||
|
organizations_url: string
|
||
|
repos_url: string
|
||
|
events_url: string
|
||
|
received_events_url: string
|
||
|
type: 'User'
|
||
|
site_admin: false
|
||
|
}
|
||
|
|
||
|
type Tier = {
|
||
|
node_id: string
|
||
|
created_at: string
|
||
|
description: string
|
||
|
monthly_price_in_cents: number
|
||
|
monthly_price_in_dollars: number
|
||
|
name: string
|
||
|
}
|
||
|
|
||
|
type WebhookPayloadSponsorship = {
|
||
|
sponsorship: {
|
||
|
node_id: string
|
||
|
created_at: string
|
||
|
maintainer: GitHubUser
|
||
|
sponsor: GitHubUser
|
||
|
privacy_level: 'public' | 'private'
|
||
|
tier: Tier
|
||
|
}
|
||
|
sender: GitHubUser
|
||
|
} & ( |
|
||
|
{
|
||
|
action: 'created'
|
||
|
} |
|
||
|
{
|
||
|
action: 'edited'
|
||
|
changes: {
|
||
|
privacy_level: 'public' | 'private'
|
||
|
}
|
||
|
} |
|
||
|
{
|
||
|
action: 'tier_changed' // upgrade
|
||
|
changes: {
|
||
|
tier: {
|
||
|
from: Tier
|
||
|
}
|
||
|
}
|
||
|
} |
|
||
|
{
|
||
|
action: 'pending_tier_change' // downgrade
|
||
|
changes: {
|
||
|
tier: {
|
||
|
from: Tier
|
||
|
}
|
||
|
}
|
||
|
effective_date: string
|
||
|
} |
|
||
|
{
|
||
|
action: 'pending_cancellation'
|
||
|
effective_date: string
|
||
|
} |
|
||
|
{
|
||
|
action: 'cancelled'
|
||
|
})
|
||
|
|
||
|
export const handler = _handler(async event => {
|
||
|
const userId = event.pathParameters.userId
|
||
|
|
||
|
if (!userId) {
|
||
|
throw new BadRequest('Missing user id')
|
||
|
}
|
||
|
|
||
|
const user = await findUser(userId)
|
||
|
|
||
|
if (!userId) {
|
||
|
throw new BadRequest('cannot find user')
|
||
|
}
|
||
|
|
||
|
const sig = event.headers['X-Hub-Signature']
|
||
|
const githubEvent = event.headers['X-GitHub-Event']
|
||
|
const id = event.headers['X-GitHub-Delivery']
|
||
|
|
||
|
if (!sig) {
|
||
|
throw new BadRequest('No X-Hub-Signature found on request')
|
||
|
}
|
||
|
|
||
|
if (!githubEvent) {
|
||
|
throw new BadRequest('No X-Github-Event found on request')
|
||
|
}
|
||
|
|
||
|
if (!id) {
|
||
|
throw new BadRequest('No X-Github-Delivery found on request')
|
||
|
}
|
||
|
|
||
|
const calculatedSig = signRequestBody(user.token, event.body)
|
||
|
|
||
|
if (sig !== calculatedSig) {
|
||
|
throw new BadRequest(
|
||
|
"X-Hub-Signature incorrect. Github webhook token doesn't match"
|
||
|
)
|
||
|
}
|
||
|
|
||
|
/* eslint-disable */
|
||
|
console.log('---------------------------------')
|
||
|
console.log(`Github-Event: "${githubEvent}"`)
|
||
|
console.log('---------------------------------')
|
||
|
console.log('Payload', event.body)
|
||
|
/* eslint-enable */
|
||
|
|
||
|
const body: unknown = JSON.parse(event.body || '{}')
|
||
|
|
||
|
// For more on events see https://developer.github.com/v3/activity/events/types/
|
||
|
switch (githubEvent) {
|
||
|
case 'sponsorship': {
|
||
|
const payload = body as WebhookPayloadSponsorship
|
||
|
|
||
|
if (payload.action === 'created' || payload.action === 'tier_changed') {
|
||
|
const data: {[key: string]: string} = {
|
||
|
access_token: user.streamlabsToken,
|
||
|
type: 'subscription',
|
||
|
message: replaceTemplate(user.message || '*GITHUB_USERNAME* just sponsored!', payload)
|
||
|
}
|
||
|
|
||
|
if (user.image_href) {
|
||
|
data.image_href = user.image_href
|
||
|
}
|
||
|
|
||
|
if (user.sound_href) {
|
||
|
data.sound_href = user.sound_href
|
||
|
}
|
||
|
|
||
|
if (user.duration) {
|
||
|
data.duration = user.duration
|
||
|
}
|
||
|
|
||
|
if (user.special_text_color) {
|
||
|
data.special_text_color = user.special_text_color
|
||
|
}
|
||
|
|
||
|
if (user.special_text_color) {
|
||
|
data.special_text_color = user.special_text_color
|
||
|
}
|
||
|
|
||
|
if (user.user_message) {
|
||
|
data.user_message = replaceTemplate(user.user_message, payload)
|
||
|
}
|
||
|
|
||
|
await fetch(`https://streamlabs.com/api/v1.0/alerts`, {
|
||
|
method: 'POST',
|
||
|
headers: {
|
||
|
Accept: 'application/json',
|
||
|
'Content-Type': 'application/json',
|
||
|
},
|
||
|
body: qs.stringify(data),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return { message: 'pong' }
|
||
|
}
|
||
|
default:
|
||
|
return { message: 'pong' }
|
||
|
}
|
||
|
})
|