Files
automata/src/index.ts
SrIzan10 40c6791dd4 feat: feedback brought back to v2 (#16)
* feat: feedback brought back to v2

* chore: remove migrate file and ts-node
2024-06-25 17:12:03 +02:00

178 lines
5.9 KiB
TypeScript

import { createClient } from '@libsql/client';
import { IssueCommentEvent } from '@octokit/webhooks-types';
import { drizzle } from 'drizzle-orm/libsql';
import { Hono } from 'hono'
import { serve } from '@hono/node-server'
import { Octokit } from 'octokit';
import * as schema from './db/schema.js'
import 'dotenv/config'
import { and, eq } from 'drizzle-orm';
import validateJsonWebhook from './util/validateJsonWebhook.js';
import { react, Reaction } from './util/react.js';
import { ZFeedbackSchema } from './util/zod.js';
const devTeam = [
'SrIzan10',
'jacoobes',
'EvolutionX-10',
'Murtatrxx',
'DuroCodes'
]
const turso = createClient({
url: process.env.DB_URL!,
authToken: process.env.DB_TOKEN!,
})
export const octokit = new Octokit({ auth: process.env.GH_API_TOKEN! });
export const db = drizzle(turso, { schema })
const app = new Hono()
app.get('/', (c) => {
return c.json({ message: 'Hello Hono!' })
})
app.post('/ev/readyToMerge', async (c) => {
const validate = await validateJsonWebhook(c)
if (!validate)
return c.json({
success: false,
error: 'Invalid token'
}, 401)
const body = await c.req.json() as IssueCommentEvent
const lcComment = body.comment.body.toLowerCase()
if (!lcComment.includes('@sernbot'))
return c.json({ ok: false, message: 'not mentioned' }, 418)
if (!devTeam.includes(body.comment.user.login))
return c.json({ ok: false, message: 'not part of devteam' }, 418)
const bodyUrl = body.issue.html_url.split('/')
let isRTM = null
if (bodyUrl[bodyUrl.length - 2] !== 'pull')
return c.json({ ok: false, message: 'not a pull request' }, 418)
if (lcComment.includes('ready to merge')) {
isRTM = true
} else if (lcComment.includes('approve merge')) {
isRTM = false
}
if (typeof isRTM !== 'boolean')
return c.json({ ok: false, message: 'does not contain what i\'m looking for' }, 418)
switch (isRTM ? 'readyToMerge' : 'approval') {
case 'readyToMerge':
await db.insert(schema.rtmAuthor).values({
authorId: body.comment.user.id.toString(),
repo: body.repository.name,
issueNumber: body.issue.number.toString(),
}).execute()
await octokit.rest.issues.createComment({
body: `Hey @${body.sender.login}! I only need one more approval by the dev team before I can merge this PR.\nJust run the command \`approve merge\`.`,
issue_number: body.issue.number,
owner: body.repository.owner.login,
repo: body.repository.name,
})
break
case 'approval':
const dbFetch = (await db
.select()
.from(schema.rtmAuthor)
.where(
and(
eq(schema.rtmAuthor.issueNumber, body.issue.number.toString()),
eq(schema.rtmAuthor.repo, body.repository.name)
)
)
.execute())[0];
if (!dbFetch)
return c.json({ ok: false, message: 'no PR found' }, 418)
if (dbFetch.authorId === body.comment.user.login.toString() && !body.comment.body.toLowerCase().includes('force')) {
await react(body.repository.owner.login, body.repository.name, body.comment.id, Reaction.MINUS_ONE)
return c.json({ ok: false, message: 'you cannot approve your own PR' }, 418)
}
await db.update(schema.rtmAuthor).set({ mergerId: body.comment.user.id.toString() }).where(
and(
eq(schema.rtmAuthor.issueNumber, body.issue.number.toString()),
eq(schema.rtmAuthor.repo, body.repository.name)
)
).execute()
await react(body.repository.owner.login, body.repository.name, body.comment.id, Reaction.PLUS_ONE)
await octokit.rest.issues.createComment({
body: `🚀 PR approved by @${body.comment.user.login}! Merging...`,
issue_number: body.issue.number,
owner: body.repository.owner.login,
repo: body.repository.name,
})
await octokit.rest.pulls.merge({
pull_number: body.issue.number,
owner: body.repository.owner.login,
repo: body.repository.name,
merge_method: 'squash',
})
break;
}
return c.json({ ok: true })
})
app.post('/ev/updateDocsJson', async (c) => {
const validate = await validateJsonWebhook(c)
if (!validate)
return c.json({
success: false,
error: 'Invalid token'
}, 401)
if ((await c.req.json()).action !== 'released')
return c.json({
success: true,
error: 'Token valid, but ignoring action...'
}, 418)
await octokit.request('POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches', {
owner: 'sern-handler',
repo: 'automata',
workflow_id: 'website-bot-update.yml',
ref: 'main',
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
})
return c.json({ success: true })
})
app.post('/web/feedback', async (c) => {
const zodValidate = ZFeedbackSchema.safeParse(await c.req.json())
if (!zodValidate.success)
return c.json({ success: false, error: zodValidate.error }, 400)
const body = zodValidate.data
const turnstileFormData = new URLSearchParams()
turnstileFormData.append('response', body.turnstileToken)
turnstileFormData.append('secret', process.env.TURNSTILE_SECRET!)
const turnstileResponse = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
body: turnstileFormData
}).then(res => res.json())
if (!turnstileResponse.success)
return c.json({ success: false, error: 'Unsuccessful turnstile verification'}, 403)
const { turnstileToken, ...dbFeedback } = body
db.insert(schema.feedback).values(dbFeedback).execute()
})
const port = Number(process.env.PORT || 3000)
serve({
fetch: app.fetch,
port,
}).on('listening', async () => {
console.log(`Hono is listening on port ${port}`)
console.log(`Github login as ${(await octokit.rest.users.getAuthenticated()).data.login}`)
})
process.stdin.on('data', async (data) => {
if (data.toString().trim() === 'test thing') {
console.log('Testing thing')
// leaving for other testing purposes
}
})