fix: script

This commit is contained in:
Saksham
2025-11-23 18:43:07 +05:30
parent ca5e93f221
commit 6242c6ca8b

View File

@@ -1,24 +1,25 @@
import { Bot, InlineKeyboard } from "grammy"; import { Bot, InlineKeyboard } from "grammy";
import { z } from "zod"; import { z } from "zod";
const EnvSchema = z.object({ const BOT_TOKEN = process.env.BOT_TOKEN!;
BOT_TOKEN: z.string(), const GROUP_ID = Number(process.env.TELEGRAM_GROUP_ID!);
TELEGRAM_GROUP_ID: z.string(), const TOPIC_COMMITS = Number(process.env.TELEGRAM_TOPIC_COMMITS!);
TELEGRAM_TOPIC_COMMITS: z.string(), const TOPIC_PRS = Number(process.env.TELEGRAM_TOPIC_PRS!);
TELEGRAM_TOPIC_PRS: z.string(), const GITHUB_TOKEN = process.env.GITHUB_TOKEN!;
GITHUB_TOKEN: z.string(), const EVENT_PATH = process.env.GITHUB_EVENT_PATH!;
GITHUB_EVENT_PATH: z.string(),
});
const env = EnvSchema.parse(process.env);
const bot = new Bot(env.BOT_TOKEN); const bot = new Bot(BOT_TOKEN);
const FileSchema = z.object({
path: z.string(),
});
const PullRequestSchema = z.object({ const PullRequestSchema = z.object({
action: z.string(), action: z.string(),
number: z.number(), number: z.number(),
repository: z.object({ repository: z.object({
full_name: z.string(), full_name: z.string(),
html_url: z.url(), html_url: z.url()
}), }),
pull_request: z.object({ pull_request: z.object({
url: z.url(), url: z.url(),
@@ -26,74 +27,55 @@ const PullRequestSchema = z.object({
body: z.string().nullable(), body: z.string().nullable(),
user: z.object({ user: z.object({
login: z.string(), login: z.string(),
html_url: z.url(), html_url: z.url()
}), }),
head: z.object({ ref: z.string() }), head: z.object({ ref: z.string() }),
base: z.object({ ref: z.string() }), base: z.object({ ref: z.string() }),
changed_files: z.number(), changed_files: z.number(),
additions: z.number(), additions: z.number(),
deletions: z.number(), deletions: z.number()
}), })
}); });
const PushSchema = z.object({ const PushSchema = z.object({
ref: z.string(), ref: z.string(),
repository: z.object({ repository: z.object({
full_name: z.string(), full_name: z.string(),
html_url: z.url(), html_url: z.url()
}), }),
head_commit: z head_commit: z.object({
.object({ id: z.string(),
id: z.string(), url: z.url(),
url: z.url(), message: z.string(),
message: z.string(), author: z.object({
author: z.object({ name: z.string(),
name: z.string(), email: z.email()
email: z.email(), }),
}), added: z.array(z.string()),
added: z.array(z.string()), modified: z.array(z.string()),
modified: z.array(z.string()), removed: z.array(z.string())
removed: z.array(z.string()), }).nullable()
})
.nullable(),
}); });
function detectLanguage(files: string[]): string { function detectLanguage(files: string[]): string {
const extCount = files.reduce<Record<string, number>>((count, file) => { const ext = files.map(f => f.split(".").pop()?.toLowerCase() || "");
const ext = file.split(".").pop()?.toLowerCase() ?? ""; if (ext.includes("kt") || ext.includes("kts")) return "Kotlin";
count[ext] = (count[ext] || 0) + 1; if (ext.includes("c") && !ext.includes("h")) return "C";
return count; if (ext.includes("rs")) return "Rust";
}, {}); if (ext.includes("sh")) return "Shell";
if (extCount.kt || extCount.kts) return "Kotlin"; if (ext.includes("ts") || ext.includes("tsx")) return "TypeScript";
if (extCount.c && !extCount.h) return "C";
if (extCount.rs) return "Rust";
if (extCount.sh) return "Shell";
if (extCount.ts || extCount.tsx) return "TypeScript";
return "Unknown"; return "Unknown";
} }
interface GitHubPRFilesResponse { async function fetchPrFiles(repoFullName: string, prNumber: number): Promise<string[]> {
data: {
repository: {
pullRequest: {
files: {
nodes: { path: string }[];
};
};
};
};
}
async function fetchPrFiles(
repoFullName: string,
prNumber: number,
): Promise<string[]> {
const query = ` const query = `
query($owner: String!, $name: String!, $number: Int!) { query($owner: String!, $name: String!, $number: Int!) {
repository(owner: $owner, name: $name) { repository(owner: $owner, name: $name) {
pullRequest(number: $number) { pullRequest(number: $number) {
files(first: 100) { files(first: 100) {
nodes { path } nodes {
path
}
} }
} }
} }
@@ -102,25 +84,17 @@ async function fetchPrFiles(
const resp = await fetch("https://api.github.com/graphql", { const resp = await fetch("https://api.github.com/graphql", {
method: "POST", method: "POST",
headers: { headers: {
Authorization: `bearer ${env.GITHUB_TOKEN}`, "Authorization": `bearer ${GITHUB_TOKEN}`,
"Content-Type": "application/json", "Content-Type": "application/json"
}, },
body: JSON.stringify({ body: JSON.stringify({ query, variables: { owner, name, number: prNumber } })
query,
variables: { owner, name, number: prNumber },
}),
}); });
if (!resp.ok) { const json = await resp.json() as any;
throw new Error(`GitHub API error: ${resp.status} ${resp.statusText}`); const nodes = json.data.repository.pullRequest.files.nodes as { path: string }[];
} return nodes.map(n => n.path);
const json = (await resp.json()) as GitHubPRFilesResponse;
if (!json?.data?.repository?.pullRequest?.files?.nodes) return [];
return json.data.repository.pullRequest.files.nodes.map((n) => n.path);
} }
async function formatPrMessage( async function formatPrMessage(evt: z.infer<typeof PullRequestSchema>): Promise<string> {
evt: z.infer<typeof PullRequestSchema>,
): Promise<string> {
const files = await fetchPrFiles(evt.repository.full_name, evt.number); const files = await fetchPrFiles(evt.repository.full_name, evt.number);
const lang = detectLanguage(files); const lang = detectLanguage(files);
return `Repository: [${evt.repository.full_name}](${evt.repository.html_url}) return `Repository: [${evt.repository.full_name}](${evt.repository.html_url})
@@ -129,7 +103,7 @@ Author: [${evt.pull_request.user.login}](${evt.pull_request.user.html_url})
Files Changed: ${evt.pull_request.changed_files}, +${evt.pull_request.additions}/- ${evt.pull_request.deletions} Files Changed: ${evt.pull_request.changed_files}, +${evt.pull_request.additions}/- ${evt.pull_request.deletions}
Likely Language: ${lang} Likely Language: ${lang}
Title: ${evt.pull_request.title} Title: ${evt.pull_request.title}
Description: ${evt.pull_request.body ?? "_None provided_"}`; Description: ${evt.pull_request.body ?? "_None provided_"}`
} }
function formatPushMessage(evt: z.infer<typeof PushSchema>): string { function formatPushMessage(evt: z.infer<typeof PushSchema>): string {
@@ -141,10 +115,8 @@ Push event but no head commit data.`;
const details = [ const details = [
c.added.length ? `Added: ${c.added.join(", ")}` : "", c.added.length ? `Added: ${c.added.join(", ")}` : "",
c.modified.length ? `Modified: ${c.modified.join(", ")}` : "", c.modified.length ? `Modified: ${c.modified.join(", ")}` : "",
c.removed.length ? `Removed: ${c.removed.join(", ")}` : "", c.removed.length ? `Removed: ${c.removed.join(", ")}` : ""
] ].filter(Boolean).join("\n");
.filter(Boolean)
.join("\n");
const lang = detectLanguage([...c.added, ...c.modified, ...c.removed]); const lang = detectLanguage([...c.added, ...c.modified, ...c.removed]);
return `Repository: [${evt.repository.full_name}](${evt.repository.html_url}) return `Repository: [${evt.repository.full_name}](${evt.repository.html_url})
Commit: [${c.id}](${c.url}) Commit: [${c.id}](${c.url})
@@ -154,46 +126,32 @@ ${details ? details + "\n" : ""}Detected Language: ${lang}`;
} }
async function main(): Promise<void> { async function main(): Promise<void> {
const raw = await (await import("node:fs/promises")).readFile(EVENT_PATH, "utf-8");
const parsed = JSON.parse(raw);
let message: string;
let topic: number;
try { try {
const raw = await Bun.file(env.GITHUB_EVENT_PATH).text(); const prEvt = PullRequestSchema.parse(parsed);
const parsed = JSON.parse(raw); message = await formatPrMessage(prEvt);
topic = TOPIC_PRS;
let message: string; } catch {
let topic: number; const pushEvt = PushSchema.parse(parsed);
message = formatPushMessage(pushEvt);
const prResult = PullRequestSchema.safeParse(parsed); topic = TOPIC_COMMITS;
if (prResult.success) {
message = await formatPrMessage(prResult.data);
topic = Number(env.TELEGRAM_TOPIC_PRS);
} else {
const pushResult = PushSchema.safeParse(parsed);
if (!pushResult.success) {
console.error(
"Unrecognized event payload",
prResult.error,
pushResult.error,
);
process.exit(1);
}
message = formatPushMessage(pushResult.data);
topic = Number(env.TELEGRAM_TOPIC_COMMITS);
}
const keyboard = new InlineKeyboard().url(
"View on GitHub",
(parsed.repository as any).html_url as string,
);
await bot.api.sendMessage(Number(env.TELEGRAM_GROUP_ID), message, {
parse_mode: "Markdown",
message_thread_id: topic,
reply_markup: keyboard,
});
process.exit(0);
} catch (err) {
console.error("Fatal error in main execution:", err);
process.exit(1);
} }
const keyboard = new InlineKeyboard().url("View on GitHub", (parsed.repository as any).html_url as string);
await bot.api.sendMessage(GROUP_ID, message, {
parse_mode: "Markdown",
message_thread_id: topic,
reply_markup: keyboard
});
process.exit(0);
} }
void main(); main().catch(err => {
console.error(err);
process.exit(1);
});