From 8266b8ae4647c6d7574f42155290db015df324ff Mon Sep 17 00:00:00 2001 From: ehsanKey Date: Fri, 10 Jul 2026 10:01:18 +0330 Subject: [PATCH 1/5] feat(utils): refactor getUser to include telegramStatus --- src/lib/github/webhooks/handlers/_utils.ts | 45 +++++++++++++++------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/lib/github/webhooks/handlers/_utils.ts b/src/lib/github/webhooks/handlers/_utils.ts index 8635ca9..92e3bcb 100644 --- a/src/lib/github/webhooks/handlers/_utils.ts +++ b/src/lib/github/webhooks/handlers/_utils.ts @@ -4,9 +4,16 @@ import type { components } from "@octokit/openapi-webhooks-types"; import { bot } from "#bot"; import { db, schema } from "#db"; +import { escapeHtml } from "../../../escape-html.ts"; + export interface User { - user: string; - userUrl: string; + ghUsername: string; + ghProfileUrl: string; + ghDisplayname: string; + tgId?: number | null; + tgUsername?: string | null; + tgDisplayName?: string | null; + telegramStatus: string; } type SimpleUser = Pick; @@ -35,24 +42,36 @@ export async function isUserMuted(githubUsername: string): Promise { return !!contributor?.isMuted; } -export async function getUser(simpleUser: SimpleUser): Promise { - const ghUsername = simpleUser.login; - - let user = simpleUser.name ?? ghUsername; - let userUrl = simpleUser.html_url; +export async function getUser(githubUser: SimpleUser): Promise { + const ghUsername = githubUser.login; - const dbUser = await db.query.contributors.findFirst({ + const contributor = await db.query.contributors.findFirst({ where: (f, o) => o.eq(f.ghUsername, ghUsername), }); - if (!dbUser?.ghUsername) { + if (!contributor) { await db.insert(schema.contributors).values({ ghUsername }); } - if (dbUser?.tgId && dbUser?.tgName) { - user = dbUser.tgName; - userUrl = `tg://user?id=${dbUser.tgId}`; + let telegramStatus = "(-)"; + const isLinked = contributor?.tgId || contributor?.tgUsername; + if (isLinked) { + if (contributor.tgUsername) { + telegramStatus = `(@${escapeHtml(contributor.tgUsername)})`; + } else if (contributor.tgName) { + telegramStatus = `(${escapeHtml(contributor.tgName)})`; + } else { + telegramStatus = `(-)`; + } } - return { user, userUrl }; + return { + ghUsername, + ghProfileUrl: githubUser.html_url, + ghDisplayname: githubUser.name ?? ghUsername, + tgId: contributor?.tgId, + tgUsername: contributor?.tgUsername, + tgDisplayName: contributor?.tgName, + telegramStatus, + }; } From 28841d496cfd0c579cfaf66c67bdbb8f50114878 Mon Sep 17 00:00:00 2001 From: ehsanKey Date: Fri, 10 Jul 2026 10:03:18 +0330 Subject: [PATCH 2/5] feat(webhooks): integrate telegramStatus into event handlers --- src/lib/github/webhooks/handlers/issues-assigned.ts | 5 +++-- src/lib/github/webhooks/handlers/issues-opened.ts | 5 +++-- src/lib/github/webhooks/handlers/pull-request-closed.ts | 5 +++-- src/lib/github/webhooks/handlers/pull-request-opened.ts | 5 +++-- src/lib/github/webhooks/handlers/star-created.ts | 8 ++++---- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/lib/github/webhooks/handlers/issues-assigned.ts b/src/lib/github/webhooks/handlers/issues-assigned.ts index fb7ed80..79afdd7 100644 --- a/src/lib/github/webhooks/handlers/issues-assigned.ts +++ b/src/lib/github/webhooks/handlers/issues-assigned.ts @@ -20,8 +20,9 @@ export const issuesAssignedCallback: HandlerFunction<"issues.assigned", unknown> await bot.announce( botText("e_issue_assigned", { issueTitle: escapeHtml(issue.title), - assignee: escapeHtml(assignee.user), - assigneeUrl: assignee.userUrl, + assignee: escapeHtml(assignee.ghDisplayname), + telegramStatus: assignee.telegramStatus, + assigneeUrl: assignee.ghProfileUrl, issueUrl: issue.html_url, repoHashtag: escapeHtml(repoHashtag), }), diff --git a/src/lib/github/webhooks/handlers/issues-opened.ts b/src/lib/github/webhooks/handlers/issues-opened.ts index 1112411..76f8483 100644 --- a/src/lib/github/webhooks/handlers/issues-opened.ts +++ b/src/lib/github/webhooks/handlers/issues-opened.ts @@ -13,8 +13,9 @@ export const issuesOpenedCallback: HandlerFunction<"issues.opened", unknown> = a await bot.announce( botText("e_issue_opened", { issueTitle: escapeHtml(issue.title), - user: escapeHtml(user.user), - userUrl: escapeHtml(user.userUrl), + user: escapeHtml(user.ghDisplayname), + telegramStatus: user.telegramStatus, + userUrl: escapeHtml(user.ghProfileUrl), issueUrl: escapeHtml(issue.html_url), repoHashtag: escapeHtml(repoHashtag), }), diff --git a/src/lib/github/webhooks/handlers/pull-request-closed.ts b/src/lib/github/webhooks/handlers/pull-request-closed.ts index c26cacc..44263a5 100644 --- a/src/lib/github/webhooks/handlers/pull-request-closed.ts +++ b/src/lib/github/webhooks/handlers/pull-request-closed.ts @@ -14,8 +14,9 @@ export const pullRequestClosedCallback: HandlerFunction<"pull_request.closed", u await bot.announce( botText("e_pull_request_closed_merged", { - user: escapeHtml(user.user), - userUrl: escapeHtml(user.userUrl), + user: escapeHtml(user.ghDisplayname), + telegramStatus: user.telegramStatus, + userUrl: escapeHtml(user.ghProfileUrl), prUrl: escapeHtml(pr.html_url), repoHashtag: escapeHtml(repoHashtag), }), diff --git a/src/lib/github/webhooks/handlers/pull-request-opened.ts b/src/lib/github/webhooks/handlers/pull-request-opened.ts index 278fdc8..4e0c80b 100644 --- a/src/lib/github/webhooks/handlers/pull-request-opened.ts +++ b/src/lib/github/webhooks/handlers/pull-request-opened.ts @@ -13,8 +13,9 @@ export const pullRequestOpenedCallback: HandlerFunction<"pull_request.opened", u await bot.announce( botText("e_pull_request_opened", { prTitle: escapeHtml(pr.title), - user: escapeHtml(user.user), - userUrl: escapeHtml(user.userUrl), + user: escapeHtml(user.ghDisplayname), + telegramStatus: user.telegramStatus, + userUrl: escapeHtml(user.ghProfileUrl), prUrl: escapeHtml(pr.html_url), repoHashtag: escapeHtml(repoHashtag), }), diff --git a/src/lib/github/webhooks/handlers/star-created.ts b/src/lib/github/webhooks/handlers/star-created.ts index 7eeac5f..c30c872 100644 --- a/src/lib/github/webhooks/handlers/star-created.ts +++ b/src/lib/github/webhooks/handlers/star-created.ts @@ -7,19 +7,19 @@ import { botText, getRepoHashtag, getUser } from "./_utils.ts"; export const starCreatedCallback: HandlerFunction<"star.created", unknown> = async (event) => { const user = await getUser(event.payload.sender); - const githubUrl = event.payload.sender.html_url; const repo = event.payload.repository; const repoHashtag = getRepoHashtag(repo.name); await bot.announce( botText("e_star_created", { - user: escapeHtml(user.user), - userUrl: escapeHtml(githubUrl), + user: escapeHtml(user.ghDisplayname), + userUrl: escapeHtml(user.ghProfileUrl), + telegramStatus: user.telegramStatus, repoName: escapeHtml(repo.name), repoUrl: escapeHtml(repo.html_url), repoHashtag: escapeHtml(repoHashtag), starNumber: repo.stargazers_count, }), - { link_preview_options: { prefer_small_media: true, url: githubUrl } }, + { link_preview_options: { prefer_small_media: true, url: user.ghProfileUrl } }, ); }; From bebcc564c7df4716e8125b8742da23b59a5c7ca1 Mon Sep 17 00:00:00 2001 From: ehsanKey Date: Fri, 10 Jul 2026 10:06:10 +0330 Subject: [PATCH 3/5] feat(messages): update translation templates for telegramStatus --- locales/en.ftl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/locales/en.ftl b/locales/en.ftl index aff30f4..648d79d 100644 --- a/locales/en.ftl +++ b/locales/en.ftl @@ -161,7 +161,7 @@ cmd_unmute = 🔊 User unMuted successfully! e_issue_opened = 🔘 Issue: { $issueTitle }. - 👤 Author: { $user } + 👤 Author: { $user } { $telegramStatus } — { $issueUrl } @@ -170,7 +170,7 @@ e_issue_opened = e_pull_request_closed_merged = 🌳 PR Merged! - 👤 Author: { $user } + 👤 Author: { $user } { $telegramStatus } — { $prUrl } @@ -179,7 +179,7 @@ e_pull_request_closed_merged = e_pull_request_opened = 🌴 PR Created: { $prTitle } - 👤 Author: { $user }! + 👤 Author: { $user } { $telegramStatus }! — { $prUrl } @@ -211,14 +211,14 @@ e_repository_created = { $repoHashtag } #new_repo e_star_created = - 🌟 { $user } gave star number { $starNumber } to { $repoName }. + 🌟 { $user } { $telegramStatus } gave star number { $starNumber } to { $repoName }. { $repoHashtag } #star e_issue_assigned = 👥 Issue Assigned: { $issueTitle }. - 👤 Assignee: { $assignee } + 👤 Assignee: { $assignee } { $telegramStatus } — { $issueUrl } From 14868423b107954b6f430f8edcede3d5e79920ee Mon Sep 17 00:00:00 2001 From: ehsanKey Date: Fri, 10 Jul 2026 10:16:13 +0330 Subject: [PATCH 4/5] feat(link): add Telegram username and display name support --- src/bot/commands/private/link.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/bot/commands/private/link.ts b/src/bot/commands/private/link.ts index a807e5f..08b22aa 100644 --- a/src/bot/commands/private/link.ts +++ b/src/bot/commands/private/link.ts @@ -16,8 +16,19 @@ export async function handler(ctx: BotContext>) { const repliedMessage = ctx.message.reply_to_message; const isActualReply = repliedMessage && !repliedMessage.forum_topic_created; - const { ghUsername, tgUsername } = ctx.args; - const tgId = isActualReply ? repliedMessage.from?.id : null; + const { ghUsername } = ctx.args; + let { tgUsername } = ctx.args; + + const from = isActualReply ? repliedMessage.from : undefined; + const tgId = from?.id ?? null; + const tgName = from ? [from.first_name, from.last_name].filter(Boolean).join(" ") : null; + + if (from?.username) { + const result = zs.tgUsername.safeParse(from.username); + if (result.success) { + tgUsername = result.data; + } + } if (!tgId && !tgUsername) { return await ctx.html.replyToMessage(ctx.t("cmd_link_no_user")); @@ -25,6 +36,7 @@ export async function handler(ctx: BotContext>) { const set: SQLiteInsertValue = { ghUsername }; if (tgId) set.tgId = tgId; + if (tgName) set.tgName = tgName; if (tgUsername) set.tgUsername = tgUsername; await db From 7013f91eae3386d95d6771390534c57e0c5cc0f9 Mon Sep 17 00:00:00 2001 From: ehsanKey Date: Fri, 10 Jul 2026 16:21:50 +0330 Subject: [PATCH 5/5] style: fix prettier formatting --- src/bot/bot.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/bot/bot.ts b/src/bot/bot.ts index 9b8952b..c0cbdd5 100644 --- a/src/bot/bot.ts +++ b/src/bot/bot.ts @@ -81,9 +81,7 @@ export class Bot extends GrammyBot { this.use(userCommands); - const isAdmin = (ctx: Context) => - ctx.from !== undefined && - config.bot.adminIds.includes(ctx.from.id); + const isAdmin = (ctx: Context) => ctx.from !== undefined && config.bot.adminIds.includes(ctx.from.id); this.filter(isAdmin).use(adminCommands);