|
| 1 | +import { Context } from 'hono'; |
| 2 | +import { Environment } from '../types'; |
| 3 | +import { TelegramService } from '../services/telegram'; |
| 4 | +import { MemberSheetServices } from '../services/membership-manager/member-sheet-services'; |
| 5 | +import { AllMessagesGroupsCrud } from '../crud/all-messages-groups'; |
| 6 | +import { D1DatabaseConnection } from '../crud/database'; |
| 7 | +import { escapeMarkdownV2 } from '../utils/helpers'; |
| 8 | + |
| 9 | +/** |
| 10 | + * Handler for Telegram chat join requests |
| 11 | + * This handles the logic when users request to join a group/channel |
| 12 | + */ |
| 13 | +export async function handleChatJoinRequest( |
| 14 | + update: any, |
| 15 | + c: Context<{ Bindings: Environment }> |
| 16 | +): Promise<Response> { |
| 17 | + const joinRequest = update.chat_join_request; |
| 18 | + const telegramId = joinRequest.from.id; |
| 19 | + const chatId = joinRequest.chat.id; |
| 20 | + const username = joinRequest.from.username; |
| 21 | + const firstName = joinRequest.from.first_name; |
| 22 | + const lastName = joinRequest.from.last_name || ''; |
| 23 | + const fullName = `${firstName} ${lastName}`.trim(); |
| 24 | + |
| 25 | + console.log('Received chat join request:', { |
| 26 | + chatId, |
| 27 | + chatTitle: joinRequest.chat.title, |
| 28 | + userId: telegramId, |
| 29 | + username, |
| 30 | + firstName, |
| 31 | + lastName, |
| 32 | + date: new Date(joinRequest.date * 1000).toISOString() |
| 33 | + }); |
| 34 | + |
| 35 | + // Store the join request in all_messages_groups table |
| 36 | + await storeJoinRequest(joinRequest, fullName, username, c.env); |
| 37 | + |
| 38 | + const telegramService = new TelegramService(c.env); |
| 39 | + const memberSheetServices = new MemberSheetServices(c.env); |
| 40 | + |
| 41 | + // Check if the user has the bot activated (can receive messages) |
| 42 | + const hasBotActivated = await telegramService.canSendMessageToUser(telegramId); |
| 43 | + |
| 44 | + if (hasBotActivated) { |
| 45 | + // User has bot activated - check if they are verified |
| 46 | + await handleUserWithBot( |
| 47 | + telegramId, |
| 48 | + chatId, |
| 49 | + fullName, |
| 50 | + username, |
| 51 | + memberSheetServices, |
| 52 | + telegramService |
| 53 | + ); |
| 54 | + } else { |
| 55 | + // User doesn't have bot activated - send silent message to group |
| 56 | + await handleUserWithoutBot( |
| 57 | + telegramId, |
| 58 | + chatId, |
| 59 | + fullName, |
| 60 | + username, |
| 61 | + telegramService, |
| 62 | + c |
| 63 | + ); |
| 64 | + } |
| 65 | + |
| 66 | + return c.json({ ok: true }); |
| 67 | +} |
| 68 | + |
| 69 | +/** |
| 70 | + * Store join request in database |
| 71 | + */ |
| 72 | +async function storeJoinRequest( |
| 73 | + joinRequest: any, |
| 74 | + fullName: string, |
| 75 | + username: string | undefined, |
| 76 | + env: Environment |
| 77 | +): Promise<void> { |
| 78 | + try { |
| 79 | + const db = new D1DatabaseConnection(env.DB); |
| 80 | + const groupMessagesCrud = new AllMessagesGroupsCrud(db); |
| 81 | + |
| 82 | + // Create a message-like structure for the join request |
| 83 | + const joinRequestMessage = { |
| 84 | + chat: joinRequest.chat, |
| 85 | + from: joinRequest.from, |
| 86 | + date: joinRequest.date, |
| 87 | + user_chat_id: joinRequest.user_chat_id, |
| 88 | + bio: joinRequest.bio, |
| 89 | + invite_link: joinRequest.invite_link, |
| 90 | + // Mark this as a join request |
| 91 | + message_type: 'chat_join_request' |
| 92 | + }; |
| 93 | + |
| 94 | + await groupMessagesCrud.storeMessage( |
| 95 | + joinRequestMessage, |
| 96 | + `Join request from ${fullName} (@${username || 'no_username'})` |
| 97 | + ); |
| 98 | + |
| 99 | + console.log('Stored chat join request in database'); |
| 100 | + } catch (storageError) { |
| 101 | + console.error('Failed to store chat join request:', storageError); |
| 102 | + } |
| 103 | +} |
| 104 | + |
| 105 | +/** |
| 106 | + * Handle user who has the bot activated |
| 107 | + * Checks verification status and sends appropriate response |
| 108 | + */ |
| 109 | +async function handleUserWithBot( |
| 110 | + telegramId: number, |
| 111 | + chatId: number, |
| 112 | + fullName: string, |
| 113 | + username: string | undefined, |
| 114 | + memberSheetServices: MemberSheetServices, |
| 115 | + telegramService: TelegramService |
| 116 | +): Promise<void> { |
| 117 | + // Check if user is verified |
| 118 | + const member = await memberSheetServices.getMemberByTelegramId(telegramId.toString()); |
| 119 | + |
| 120 | + if (member && member.telegram_id) { |
| 121 | + // User is verified - create and send private invite link |
| 122 | + await sendPrivateInviteLink( |
| 123 | + telegramId, |
| 124 | + chatId, |
| 125 | + fullName, |
| 126 | + telegramService |
| 127 | + ); |
| 128 | + } else { |
| 129 | + // User has bot but not verified - ask them to verify |
| 130 | + await sendVerificationPrompt(telegramId, fullName, telegramService); |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +/** |
| 135 | + * Send private invite link to verified user |
| 136 | + */ |
| 137 | +async function sendPrivateInviteLink( |
| 138 | + telegramId: number, |
| 139 | + chatId: number, |
| 140 | + fullName: string, |
| 141 | + telegramService: TelegramService |
| 142 | +): Promise<void> { |
| 143 | + try { |
| 144 | + const inviteLink = await telegramService.createChatInviteLink( |
| 145 | + chatId, |
| 146 | + telegramId, |
| 147 | + fullName |
| 148 | + ); |
| 149 | + |
| 150 | + if (inviteLink) { |
| 151 | + // Send the private invite link to the user |
| 152 | + await telegramService.sendMessage( |
| 153 | + telegramId, |
| 154 | + `مرحباً ${escapeMarkdownV2(fullName)}\\! 🎉\n\n` + |
| 155 | + `تم التحقق من عضويتك بنجاح\\.\n\n` + |
| 156 | + `إليك رابط الانضمام الخاص بك:\n` + |
| 157 | + `${escapeMarkdownV2(inviteLink)}\n\n` + |
| 158 | + `⚠️ *ملاحظة مهمة:*\n` + |
| 159 | + `• هذا الرابط خاص بك فقط\n` + |
| 160 | + `• يمكن استخدامه مرة واحدة فقط\n` + |
| 161 | + `• لا تشاركه مع أي شخص آخر` |
| 162 | + ); |
| 163 | + |
| 164 | + console.log(`Sent private invite link to verified user ${telegramId}`); |
| 165 | + |
| 166 | + // Optionally approve the join request automatically |
| 167 | + // await telegramService.approveChatJoinRequest(chatId, telegramId); |
| 168 | + } else { |
| 169 | + // Failed to create invite link - fall back to manual approval |
| 170 | + await telegramService.sendMessage( |
| 171 | + telegramId, |
| 172 | + `مرحباً ${escapeMarkdownV2(fullName)}\\!\n\n` + |
| 173 | + `تم التحقق من عضويتك\\. سيتم الموافقة على طلبك قريباً\\.` |
| 174 | + ); |
| 175 | + console.log(`Failed to create invite link, sent confirmation to user ${telegramId}`); |
| 176 | + } |
| 177 | + } catch (error) { |
| 178 | + console.error('Error creating/sending invite link:', error); |
| 179 | + } |
| 180 | +} |
| 181 | + |
| 182 | +/** |
| 183 | + * Send verification prompt to unverified user |
| 184 | + */ |
| 185 | +async function sendVerificationPrompt( |
| 186 | + telegramId: number, |
| 187 | + fullName: string, |
| 188 | + telegramService: TelegramService |
| 189 | +): Promise<void> { |
| 190 | + try { |
| 191 | + await telegramService.sendMessage( |
| 192 | + telegramId, |
| 193 | + `مرحباً ${escapeMarkdownV2(fullName)}\\!\n\n` + |
| 194 | + `لقد تلقينا طلب انضمامك إلى المجموعة\\.\n\n` + |
| 195 | + `يرجى استخدام الأمر /verify للتحقق من عضويتك حتى تتمكن من الوصول إلى المجموعة\\.` |
| 196 | + ); |
| 197 | + console.log(`Sent verification message to unverified user ${telegramId}`); |
| 198 | + } catch (error) { |
| 199 | + console.error('Error sending message to user with bot activated:', error); |
| 200 | + } |
| 201 | +} |
| 202 | + |
| 203 | +/** |
| 204 | + * Handle user who doesn't have the bot activated |
| 205 | + * Sends a silent message to the group that auto-deletes |
| 206 | + */ |
| 207 | +async function handleUserWithoutBot( |
| 208 | + telegramId: number, |
| 209 | + chatId: number, |
| 210 | + fullName: string, |
| 211 | + username: string | undefined, |
| 212 | + telegramService: TelegramService, |
| 213 | + c: Context<{ Bindings: Environment }> |
| 214 | +): Promise<void> { |
| 215 | + try { |
| 216 | + const usernameText = username ? `@${escapeMarkdownV2(username)}` : ''; |
| 217 | + const messageText = `عزيزي/عزيزتي ${escapeMarkdownV2(fullName)} ${usernameText}\n\n` + |
| 218 | + `يرجى التواصل مع هذا البوت على الخاص حتى تتمكن من الانضمام إلى المجموعة\\.`; |
| 219 | + |
| 220 | + const sentMessageId = await telegramService.sendMessage( |
| 221 | + chatId, |
| 222 | + messageText, |
| 223 | + 'MarkdownV2', |
| 224 | + undefined, |
| 225 | + undefined, |
| 226 | + undefined, |
| 227 | + true // disable_notification for silent message |
| 228 | + ); |
| 229 | + |
| 230 | + console.log(`Sent silent message to group for user ${telegramId} who doesn't have bot activated`); |
| 231 | + |
| 232 | + // Schedule message deletion after 10 seconds |
| 233 | + // Note: Using waitUntil to ensure the deletion happens even after response is sent |
| 234 | + if (sentMessageId) { |
| 235 | + c.executionCtx.waitUntil( |
| 236 | + (async () => { |
| 237 | + await new Promise(resolve => setTimeout(resolve, 10000)); |
| 238 | + await telegramService.deleteMessage(chatId, sentMessageId); |
| 239 | + console.log(`Deleted notification message ${sentMessageId} from group ${chatId}`); |
| 240 | + })() |
| 241 | + ); |
| 242 | + } |
| 243 | + } catch (error) { |
| 244 | + console.error('Error sending/deleting message to group:', error); |
| 245 | + } |
| 246 | +} |
0 commit comments