### MessageX Example: Method Installation Pattern Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/ARCHITECTURE.md Demonstrates the three steps of the method installation pattern: defining an interface, creating a complete type, and exporting an installation function that injects methods. ```typescript // Step 1: Methods interface export interface MessageXFragment extends InlineMessageXFragment { delete(signal?: AbortSignal): Ret<"deleteMessage">; // ... other methods } // Step 2: Complete type export type MessageX = MessageXFragment & Message; // Step 3: Installation function export function installMessageMethods(api: RawApi, message: Message) { const methods: MessageXFragment = { delete: (signal) => api.deleteMessage({ chat_id: message.chat.id, message_id: message.message_id, }, signal), // ... implementations }; Object.assign(message, methods); } ``` -------------------------------- ### Install and Use Hydrate Plugin Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/QUICK_REFERENCE.md Install the hydrate plugin and apply it to your bot. This example shows the basic setup for a custom context type. ```typescript import { hydrate, HydrateFlavor } from "@grammyjs/hydrate"; import { Bot, Context } from "grammy"; type MyContext = HydrateFlavor; const bot = new Bot("token"); bot.use(hydrate()); ``` -------------------------------- ### Chaining Installations for Callback Queries Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/update.md This example shows how to chain multiple installations for a callback query. It handles message installation, editing messages, and inline message edits within a single event handler. ```typescript bot.on("callback_query", async (ctx) => { // CallbackQuery installation runs await ctx.callbackQuery.answer(); // If message exists, Message installation also runs if (ctx.callbackQuery.message) { await ctx.callbackQuery.message.editText("Updated"); } // If inline_message_id exists, InlineMessage methods available if (ctx.callbackQuery.inline_message_id) { await ctx.callbackQuery.editText("Inline edit"); } }); ``` -------------------------------- ### Message Method Installation Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/OVERVIEW.md Demonstrates the design pattern for installing message-specific methods, such as `delete()`, onto message objects. This pattern captures required IDs like `chat_id` and `message_id` to prefill API calls. ```typescript export interface MessageXFragment { delete(signal?: AbortSignal): Ret<"deleteMessage">; } export type MessageX = MessageXFragment & Message; export function installMessageMethods(api: RawApi, message: Message) { const methods: MessageXFragment = { delete: (signal) => api.deleteMessage({ chat_id: message.chat.id, message_id: message.message_id, }, signal), }; Object.assign(message, methods); } ``` -------------------------------- ### Middleware Ordering Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/QUICK_REFERENCE.md Demonstrates the correct order for applying session and hydration middleware before routes and handlers. ```typescript const bot = new Bot(token); // 1. Session middleware bot.use(session()); // 2. Hydration middleware bot.use(hydrate()); // 3. Routes and handlers bot.on("message", async (ctx) => { // Both session and hydration available }); ``` -------------------------------- ### Installation Dispatcher Function Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/ARCHITECTURE.md The `installUpdateMethods` function acts as a dispatcher, routing the installation of methods to the appropriate handler based on the type of update received. This ensures that only relevant methods are installed for each specific update type. ```typescript export function installUpdateMethods(api: RawApi, update: Update) { if (update.message !== undefined) { installMessageMethods(api, update.message); } else if (update.callback_query !== undefined) { installCallbackQueryMethods(api, update.callback_query); } else if (update.inline_query !== undefined) { installInlineQueryMethods(api, update.inline_query); } else if (update.shipping_query !== undefined) { installShippingQueryMethods(api, update.shipping_query); } else if (update.pre_checkout_query !== undefined) { installPreCheckoutQueryMethods(api, update.pre_checkout_query); } else if (update.chosen_inline_result !== undefined) { installChosenInlineResultMethods(api, update.chosen_inline_result); } else if (update.chat_join_request !== undefined) { installChatJoinRequestMethods(api, update.chat_join_request); } } ``` -------------------------------- ### Selective Installation of Methods Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/update.md Demonstrates how update methods are selectively installed only on present properties of the update object to optimize performance. This avoids unnecessary method installations for undefined properties. ```typescript // If update.message is undefined, no methods installed // If update.callback_query is undefined, no methods installed // etc. // Only the actually present property type gets hydrated if (update.message !== undefined) { installMessageMethods(api, update.message); } else if (update.inline_query !== undefined) { // Only one branch executes installInlineQueryMethods(api, update.inline_query); } ``` -------------------------------- ### Install Hydrate Plugin Middleware Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/OVERVIEW.md Installs the full Hydrate middleware, which hydrates both API and context objects. This is the most common way to use the plugin. ```typescript import { hydrate } from "@grammyjs/hydrate"; import { Context } from "grammy"; type MyContext = Context & hydrate.HydrateFlavor; const bot = new Bot(token); bot.use(hydrate()); ``` -------------------------------- ### Object.assign() for Method Installation Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/update.md Illustrates the use of `Object.assign()` for installing methods onto update objects, ensuring minimal overhead by directly adding properties without relying on prototype chains or getters. ```typescript const methods = { /* method implementations */ }; Object.assign(update.message, methods); // Add properties directly // No prototype chains or getters - just plain property assignment. ``` -------------------------------- ### Chat Settings Management Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/chat.md Demonstrates how to set the title, description, and permissions for a chat. This is useful for configuring group settings dynamically. ```typescript bot.on("message", async (ctx) => { // Configure chat await ctx.chat.setTitle("My Awesome Group"); await ctx.chat.setDescription("The best group ever!"); // Set permissions await ctx.chat.setPermissions({ can_send_messages: true, can_send_media_messages: true, can_send_polls: false, can_add_web_page_previews: true, can_change_info: false, can_invite_users: true, can_pin_messages: false }); }); ``` -------------------------------- ### Invite Link Management Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/chat.md Demonstrates creating a time-limited and member-limited invite link for a chat. This is useful for temporary or controlled access. ```typescript bot.on("message", async (ctx) => { if (ctx.callbackQuery?.data === "create_invite") { const link = await ctx.chat.createInviteLink({ expire_date: Math.floor(Date.now() / 1000) + 86400, // 24 hours member_limit: 10, name: "Limited Invite" }); await ctx.reply(`Invite: ${link.invite_link}`); } }); ``` -------------------------------- ### Forum Topic Management Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/chat.md Shows how to create new forum topics within a chat and log the total number of topics. Requires the chat to be a forum. ```typescript bot.on("message", async (ctx) => { // Create topics const general = await ctx.chat.createForumTopic("General"); const support = await ctx.chat.createForumTopic("Support"); // List topics (need to use getChat instead) const chat = await ctx.getChat(); console.log(`Forum topics: ${chat.forum_topic_count}`); }); ``` -------------------------------- ### Install Context-Only Hydration Middleware Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/OVERVIEW.md Installs only the context hydration middleware from the Hydrate plugin. This augments context objects with methods but does not hydrate API responses. ```typescript const middleware = hydrateContext(); bot.use(middleware); ``` -------------------------------- ### Promote User to Administrator Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/user.md Shows how to promote a user to an administrator role in a chat. Includes examples for granting all permissions and specific ones. ```typescript bot.on("message", async (ctx) => { const moderator = ctx.message.from; // Promote with all permissions await moderator.promote({ can_delete_messages: true, can_restrict_members: true, can_pin_messages: true, can_manage_topics: true }); // Promote with specific permissions await moderator.promote({ can_delete_messages: true, can_ban_chat_members: false, // Cannot ban can_restrict_members: false // Cannot restrict }); }); ``` -------------------------------- ### Track and Update Live Location Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/message.md This example shows how to send a message with a live location and then update its coordinates and other properties. ```typescript // Track live location const msg = await ctx.replyWithLocation(10.5, 20.3, { live_period: 3600 }); // Update location await msg.editLiveLocation(10.6, 20.4, { heading: 45, proximity_alert_radius: 100 }); ``` -------------------------------- ### Install API-Only Hydration Transformer Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/OVERVIEW.md Installs only the API transformer from the Hydrate plugin. This hydrates API responses but not the context objects. ```typescript const transformer = hydrateApi(); bot.api.config.use(transformer); ``` -------------------------------- ### Basic Usage of hydrate() Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/main-plugin.md Installs hydrated methods on both context objects and API call results. This is the primary entry point for using the plugin. It modifies ctx.api.config middleware and installs update methods. ```typescript import { Bot, Context } from "grammy"; import { hydrate, HydrateFlavor } from "@grammyjs/hydrate"; type MyContext = HydrateFlavor; const bot = new Bot(token); bot.use(hydrate()); bot.on("message", async (ctx) => { // ctx.message now has hydrated methods const reply = await ctx.reply("Hello"); await reply.delete(); // method added by hydrate }); ``` -------------------------------- ### Channel Post Handler Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/update.md Illustrates handling channel posts and using the specific methods available on the hydrated channel post object, such as pin. ```typescript // Channel post bot.on("channel_post", async (ctx) => { // ctx.update.channel_post is MessageX const post = ctx.channelPost; if (post) { await post.pin(); } }); ``` -------------------------------- ### Install and Use Hydrate Plugin Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/README.md Install the @grammyjs/hydrate plugin and integrate it into your grammY bot. This sets up the necessary context flavor for enhanced message operations. ```typescript import { hydrate, HydrateFlavor } from "@grammyjs/hydrate"; import { Bot, Context } from "grammy"; type MyContext = HydrateFlavor; const bot = new Bot("YOUR_BOT_TOKEN"); bot.use(hydrate()); ``` -------------------------------- ### Complete Payment Flow Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/query-methods.md This snippet shows a full payment process, from initiating an invoice to handling shipping, pre-checkout, and successful payment confirmations. Ensure you have the 'hydrate' middleware imported and configured. ```typescript import { Bot, Context } from "grammy"; import { hydrate, HydrateFlavor } from "@grammyjs/hydrate"; type MyContext = HydrateFlavor; const bot = new Bot(token); bot.use(hydrate()); // 1. User initiates payment bot.command("buy", async (ctx) => { await ctx.sendInvoice({ title: "Premium membership", description: "1 year access", payload: "premium_1year", provider_token: "YOUR_PROVIDER_TOKEN", currency: "USD", prices: [ { label: "Premium 1 year", amount: 9900 } // $99.00 ], is_flexible: true // Allow shipping }); }); // 2. Handle shipping query bot.on("shipping_query", async (ctx) => { const address = ctx.shippingQuery.shipping_address; // Calculate shipping const shippingCost = calculateShipping(address); if (shippingCost !== null) { await ctx.shippingQuery.answer(true, { shipping_options: [ { id: "standard", title: "Standard (5-7 days)", price_breakdown: [ { label: "Shipping", amount: shippingCost } ], total_amount: shippingCost } ] }); } else { await ctx.shippingQuery.answer(false, { error_message: "We don't ship to your location" }); } }); // 3. Handle pre-checkout bot.on("pre_checkout_query", async (ctx) => { const query = ctx.preCheckoutQuery; // Validate order within 10 seconds try { const order = await verifyOrder(query.invoice_payload); if (order && order.price === query.total_amount) { await ctx.preCheckoutQuery.answer(true); } else { await ctx.preCheckoutQuery.answer(false, { error_message: "Order verification failed" }); } } catch (err) { await ctx.preCheckoutQuery.answer(false, { error_message: "Verification service unavailable" }); } }); // 4. Handle successful payment bot.on("message", async (ctx) => { if (ctx.message.successful_payment) { const payment = ctx.message.successful_payment; // Process payment await processPayment({ user_id: ctx.from.id, payload: payment.invoice_payload, amount: payment.total_amount, currency: payment.currency, telegram_payment_charge_id: payment.telegram_payment_charge_id, provider_payment_charge_id: payment.provider_payment_charge_id }); await ctx.reply( "✅ Thank you for your purchase!\n" + "Your premium membership is now active." ); } }); ``` -------------------------------- ### React to Messages Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/message.md This example shows how to add single emoji, multiple reactions, custom emoji reactions, and clear reactions from a message. ```typescript bot.on("message", async (ctx) => { // Single emoji await ctx.message.react("👍"); // Multiple reactions await ctx.message.react(["👍", "❤️"]); // Custom reaction await ctx.message.react({ type: "emoji", emoji: "🎉" }); // Clear reactions await ctx.message.react([]); }); ``` -------------------------------- ### Install Update Methods Middleware Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/update.md Installs update methods and API transformers into the context for use in middleware and handlers. This function should be called early in the middleware pipeline. ```typescript export function hydrate() { const hydrator = hydrateApi(); return (ctx: HydrateFlavor, next: () => Promise) => { // 1. Install API transformer ctx.api.config.use(hydrator); // 2. Install update methods installUpdateMethods(ctx.api.raw, ctx.update); // 3. Call next middleware return next(); }; } ``` -------------------------------- ### Initialize grammY Bot with Hydrate Plugin Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/INDEX.md This shows the basic setup for a grammY bot using the hydrate plugin. It requires importing `Bot` and `hydrate` and then using `bot.use(hydrate())` to enable the plugin. ```typescript const bot = new Bot>(token); bot.use(hydrate()); // Single line to enable ``` -------------------------------- ### installUpdateMethods() Behavior Logic Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/update.md The function inspects the update object and calls the appropriate installation function for the type of object present, such as messages, inline queries, or callback queries. ```typescript if (update.message !== undefined) { installMessageMethods(api, update.message); } else if (update.channel_post !== undefined) { installMessageMethods(api, update.channel_post); } else if (update.edited_message !== undefined) { installMessageMethods(api, update.edited_message); } else if (update.edited_channel_post !== undefined) { installMessageMethods(api, update.edited_channel_post); } else if (update.inline_query !== undefined) { installInlineQueryMethods(api, update.inline_query); } else if (update.callback_query !== undefined) { installCallbackQueryMethods(api, update.callback_query); } else if (update.shipping_query !== undefined) { installShippingQueryMethods(api, update.shipping_query); } else if (update.pre_checkout_query !== undefined) { installPreCheckoutQueryMethods(api, update.pre_checkout_query); } else if (update.chosen_inline_result !== undefined) { installChosenInlineResultMethods(api, update.chosen_inline_result); } else if (update.chat_join_request !== undefined) { installChatJoinRequestMethods(api, update.chat_join_request); } ``` -------------------------------- ### Usage Pattern: Image Gallery Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/inline-query.md Example of creating an image gallery using inline queries. It fetches images based on the query and presents them as 'photo' type results with a button to view the full image. ```APIDOC ## Usage Pattern: Image Gallery ### Description This pattern shows how to build an image gallery using inline queries. It fetches images based on the user's query and returns them as photo results, including a button to view the full image. ### Code Example ```typescript bot.on("inline_query", async (ctx) => { const images = await fetchImages(ctx.inlineQuery.query); // Assume fetchImages is defined elsewhere await ctx.inlineQuery.answer( images.map((img, idx) => ({ type: "photo" as const, id: String(idx), photo_url: img.url, thumbnail_url: img.thumbUrl, title: img.title, description: img.credit, reply_markup: { inline_keyboard: [[ // Define inline keyboard markup { text: "View full", url: img.pageUrl } // Button to open URL ]] } })) ); }); ``` ``` -------------------------------- ### Join Request Flow Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/query-methods.md This snippet demonstrates how to handle chat join requests, including auto-approving whitelisted users, sending web apps for verification, and declining bots. It also shows how to process web app data for verification results. ```typescript bot.on("chat_join_request", async (ctx) => { const user = ctx.chatJoinRequest.from; const chat = ctx.chatJoinRequest.chat; // Simple auto-approve for certain users if (await isWhitelisted(user.id)) { await ctx.chatJoinRequest.approve(); console.log(`Auto-approved ${user.first_name}`); } // Require verification else if (!user.is_bot) { await ctx.chatJoinRequest.sendWebApp( `https://example.com/verify?user_id=${user.id}&chat_id=${chat.id}` ); } // Auto-decline bots (unless verified) else { await ctx.chatJoinRequest.decline(); } }); // Handle web app callback bot.on("message", async (ctx) => { if (ctx.message.web_app_data) { const data = JSON.parse(ctx.message.web_app_data.data); // Process verification result if (data.verified) { // Now we can approve the join request // (would need to store and retrieve it by user/chat ID) console.log(`User ${data.user_id} verified in chat ${data.chat_id}`); } } }); ``` -------------------------------- ### Example Usage of HydrateFlavor Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/types.md Demonstrates how to use the HydrateFlavor type to create a custom context type for a grammY bot. ```typescript type MyContext = HydrateFlavor; const bot = new Bot(token); ``` -------------------------------- ### Usage Pattern: Search Engine Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/inline-query.md Example of how to implement a search engine functionality using inline queries. It handles empty queries by showing suggestions and fetches and displays search results. ```APIDOC ## Usage Pattern: Search Engine ### Description This pattern demonstrates how to use inline queries to create a search engine. It first checks if a query is provided, shows suggestions if not, and then fetches and formats search results. ### Code Example ```typescript bot.on("inline_query", async (ctx) => { const query = ctx.inlineQuery.query.trim(); if (!query) { // No query, show suggestions await ctx.inlineQuery.answer([ { type: "article", id: "help", title: "Help", input_message_content: { message_text: "Type to search..." } } ]); return; } // Search and return results const results = await searchAPI(query); // Assume searchAPI is defined elsewhere await ctx.inlineQuery.answer( results.map((item, idx) => ({ type: "article" as const, id: String(idx), title: item.name, description: item.description, input_message_content: { message_text: item.fullText } })), { cache_time: 600 } // Cache results for 600 seconds ); }); ``` ``` -------------------------------- ### hydrate() Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/main-plugin.md Installs hydrated methods on both context objects and API call results. This is the primary entry point for using the plugin. ```APIDOC ## hydrate() ### Description Installs hydrated methods on both context objects and API call results. This is the primary entry point for using the plugin. ### Signature ```typescript function hydrate(): (ctx: HydrateFlavor, next: () => Promise) => Promise ``` ### Parameters None. Configures the middleware for the generic context type. ### Returns A grammY middleware function with signature `(ctx: HydrateFlavor, next: () => Promise) => Promise`. ### Behavior 1. Creates an API response transformer via `hydrateApi()` 2. Registers it with `ctx.api.config.use(hydrator)` to intercept all API responses 3. Installs update methods on `ctx.update` and its nested properties (message, callback_query, etc.) 4. Calls `next()` to pass control to the next middleware ### Usage ```typescript import { Bot, Context } from "grammy"; import { hydrate, HydrateFlavor } from "@grammyjs/hydrate"; type MyContext = HydrateFlavor; const bot = new Bot(token); bot.use(hydrate()); bot.on("message", async (ctx) => { // ctx.message now has hydrated methods const reply = await ctx.reply("Hello"); await reply.delete(); // method added by hydrate }); ``` ### Side Effects - Modifies `ctx.api.config` middleware chain - Mutates `ctx.update` and its properties by installing methods via `Object.assign()` - Affects all subsequent API calls in this middleware chain ``` -------------------------------- ### Member Management Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/chat.md Logs information about new members joining a chat and checks the bot's administrator status. This snippet iterates through new members and retrieves their details. ```typescript bot.on("message:new_chat_members", async (ctx) => { if (!ctx.message.new_chat_members) return; for (const member of ctx.message.new_chat_members) { const info = await ctx.chat.getMember(member.id); console.log(`Member joined: ${member.first_name}`); } // Check bot status const botInfo = await ctx.chat.getMember(ctx.botInfo.id); if (botInfo.status === "administrator") { await ctx.reply("I'm an admin here!"); } }); ``` -------------------------------- ### Handle Callback Queries and Edit Messages Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/INDEX.md This example shows how to answer a callback query and then edit the original message to indicate completion. It requires the `callback_query` event to be handled. ```typescript bot.on("callback_query", async (ctx) => { await ctx.callbackQuery.answer({ text: "Processing..." }); if (ctx.callbackQuery.message) { await ctx.callbackQuery.message.editText("Done!"); } }); ``` -------------------------------- ### Error Handling Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/chat.md Shows how to handle potential errors when interacting with chat methods, specifically checking for `CHAT_ADMIN_REQUIRED` and `CHAT_NOT_FOUND` errors. ```typescript bot.on("message", async (ctx) => { try { const count = await ctx.chat.getMembersCount(); } catch (err) { if (err instanceof GrammyError) { if (err.description.includes("CHAT_ADMIN_REQUIRED")) { await ctx.reply("I need admin rights"); } else if (err.description.includes("CHAT_NOT_FOUND")) { await ctx.reply("Chat not found"); } } } }); ``` -------------------------------- ### Automatic Installation of UserX Methods Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/user.md UserX methods are automatically available on user objects when obtained via `ctx.getChatMember()` or `ctx.chat.getMember()`, provided the API response includes a `chat_id` and the `user` property is augmented. ```typescript // Methods are automatically installed when you call: const member = await ctx.getChatMember(userId); await member.user.ban(); // UserX methods available // Or from chat context: const member = await ctx.chat.getMember(userId); await member.user.restrict({ can_send_messages: false }); ``` -------------------------------- ### Respond to Inline Queries with Simple Article Results Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/inline-query.md Use `ctx.inlineQuery.answer` to send back an array of inline query results. This example shows how to provide simple article results. ```typescript bot.on("inline_query", async (ctx) => { const { query } = ctx.inlineQuery; // Simple article results await ctx.inlineQuery.answer([ { type: "article", id: "1", title: "Article 1", description: "First article", input_message_content: { message_text: "Article 1 content" } }, { type: "article", id: "2", title: "Article 2", description: "Second article", input_message_content: { message_text: "Article 2 content" } } ]); }); ``` -------------------------------- ### installUpdateMethods() Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/update.md Installs methods onto the nested properties of an Update object, enabling direct method calls on entities like messages, queries, and requests. ```APIDOC ## Function - installUpdateMethods() Called by the hydrate plugin to install methods on update objects. ### Signature ```typescript function installUpdateMethods(api: RawApi, update: Update): void ``` ### Parameters | Parameter | Type | Description | |-----------|------|-------------| | `api` | `RawApi` | The raw API connector for making API calls | | `update` | `Update` | The update object to process | ### Behavior The function inspects the update object and calls the appropriate installation function for whatever type of object is present: ```typescript if (update.message !== undefined) { installMessageMethods(api, update.message); } else if (update.channel_post !== undefined) { installMessageMethods(api, update.channel_post); } else if (update.edited_message !== undefined) { installMessageMethods(api, update.edited_message); } else if (update.edited_channel_post !== undefined) { installMessageMethods(api, update.edited_channel_post); } else if (update.inline_query !== undefined) { installInlineQueryMethods(api, update.inline_query); } else if (update.callback_query !== undefined) { installCallbackQueryMethods(api, update.callback_query); } else if (update.shipping_query !== undefined) { installShippingQueryMethods(api, update.shipping_query); } else if (update.pre_checkout_query !== undefined) { installPreCheckoutQueryMethods(api, update.pre_checkout_query); } else if (update.chosen_inline_result !== undefined) { installChosenInlineResultMethods(api, update.chosen_inline_result); } else if (update.chat_join_request !== undefined) { installChatJoinRequestMethods(api, update.chat_join_request); } ``` ``` -------------------------------- ### Restrict User Permissions Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/user.md Demonstrates how to restrict a user's ability to send messages, media, or previews. Includes examples for temporary mutes and full unmuting. ```typescript bot.on("message", async (ctx) => { const user = ctx.message.from; // Mute user (only send text) await user.restrict({ can_send_messages: true, can_send_media_messages: false, can_send_polls: false }); // Mute completely await user.restrict({ can_send_messages: false, can_send_media_messages: false, can_send_other_messages: false, can_add_web_page_previews: false }); // Temporary mute (1 hour) const oneHourLater = Math.floor(Date.now() / 1000) + 3600; await user.restrict({ can_send_messages: false }, { until_date: oneHourLater }); // Allow after previous restriction await user.restrict({ can_send_messages: true, can_send_media_messages: true, can_send_polls: true, can_send_other_messages: true, can_add_web_page_previews: true, can_change_info: true, can_invite_users: true, can_pin_messages: true }); }); ``` -------------------------------- ### Inline Query Response Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/QUICK_REFERENCE.md Answer an inline query by providing a list of results. This example shows how to configure cache time for the results. ```typescript await inlineQuery.answer([ { type: "article", id: "1", title: "Result", input_message_content: { message_text: "Content" } } ], { cache_time: 600 }); ``` -------------------------------- ### Message Handler Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/update.md Demonstrates how to access and use hydrated message methods within a regular message handler. All message methods like delete, editText, forward, and react are available. ```typescript // Regular message bot.on("message", async (ctx) => { // ctx.update.message is MessageX const msg = ctx.message; if (msg) { // All message methods available await msg.delete(); await msg.editText("Edited"); await msg.forward(otherChatId); await msg.react("👍"); } }); ``` -------------------------------- ### installUpdateMethods() Function Signature Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/update.md This function is called by the hydrate plugin to install methods onto update objects. It takes the raw API connector and the update object as parameters. ```typescript function installUpdateMethods(api: RawApi, update: Update): void ``` -------------------------------- ### Handle Inline Query and Edit Message Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/chosen-inline-result.md This example demonstrates how to answer an inline query and then use the `chosenInlineResult` context to edit the sent message, creating a countdown timer effect. ```typescript bot.on("inline_query", async (ctx) => { await ctx.inlineQuery.answer([ { type: "article", id: "timer", title: "Start Timer", input_message_content: { message_text: "⏱️ Timer: 60 seconds remaining" }, reply_markup: { inline_keyboard: [[ { text: "Cancel", callback_data: "cancel_timer" } ]] } } ]); }); bot.on("chosen_inline_result", async (ctx) => { if (!ctx.chosenInlineResult.inline_message_id) return; let seconds = 60; const interval = setInterval(async () => { seconds--; if (seconds > 0) { await ctx.chosenInlineResult.editText( `⏱️ Timer: ${seconds} seconds remaining` ); } else { await ctx.chosenInlineResult.editText("⏱️ Time's up!"); clearInterval(interval); } }, 1000); }); ``` -------------------------------- ### Hydration Site Logic Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/ARCHITECTURE.md Conditional logic to call the appropriate installation function based on the detected type of the API response result. This ensures the correct methods are injected into the hydrated objects. ```typescript if (isMessage(res.result)) { installMessageMethods(toApi(prev), res.result); } else if (isInlineMessage(res.result)) { installInlineMessageMethods(toApi(prev), res.result); } else if (isChatMember(res.result) && hasChatId(payload)) { installUserMethods(toApi(prev), res.result.user, payload.chat_id); } else if (isChat(res.result)) { installChatMethods(toApi(prev), res.result); } ``` -------------------------------- ### sendWebApp() Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/query-methods.md Shows a Mini App to the user before deciding on the join request. The bot must be an administrator with the 'can_invite_users' permission. ```APIDOC ## sendWebApp() ### Description Show a Mini App to the user before deciding on the join request. ### Method `sendWebApp(web_app_url: string, signal?: AbortSignal): Promise` ### Parameters #### Path Parameters - `web_app_url` (string) - Required - The URL of the Mini App to be opened - `signal` (AbortSignal) - Optional - Optional signal to cancel the request ### Returns `Promise` - Always returns true on success. ### Example ```typescript bot.on("chat_join_request", async (ctx) => { // Show verification web app await ctx.chatJoinRequest.sendWebApp( "https://example.com/verify?user_id=" + ctx.chatJoinRequest.from.id ); }); ``` ``` -------------------------------- ### hydrateContext() Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/main-plugin.md Installs hydrated methods only on context update objects, without transforming API responses. ```APIDOC ## hydrateContext() ### Description Installs hydrated methods only on context update objects, without transforming API responses. ### Signature ```typescript function hydrateContext(): ( ctx: HydrateFlavor, next: () => Promise ) => Promise ``` ### Parameters None. ### Returns A grammY middleware function. ### Behavior 1. Calls `installUpdateMethods()` on `ctx.update` and its properties 2. Does NOT install API transformer 3. Useful when API responses are already hydrated (e.g., by another middleware) ### Usage ```typescript // Separate API and context hydration const bot = new Bot>(token); // API hydration via transformer bot.api.config.use(hydrateApi()); // Context hydration via middleware bot.use(hydrateContext()); bot.on("message", async (ctx) => { // Both ctx.update and API response objects are hydrated await ctx.message.delete(); }); ``` ### When to Use - When you've already set up API transformation elsewhere - When you only care about context methods, not API response methods - For fine-grained control over middleware ordering ``` -------------------------------- ### getMembersCount() Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/chat.md Get the number of members in the chat. This method returns the total count of members currently in the chat. ```APIDOC ## getMembersCount() ### Description Get the number of members in the chat. ### Signature ```typescript getMembersCount( other?: Other<"getChatMemberCount">, signal?: AbortSignal, ): Promise ``` ### Parameters #### Path Parameters - **other** (Other<"getChatMemberCount">) - Optional - Additional parameters (rarely used) - **signal** (AbortSignal) - Optional - Optional signal to cancel the request ### Returns `Promise` - Number of members in the chat. ### Example ```typescript bot.on("message", async (ctx) => { const count = await ctx.chat.getMembersCount(); await ctx.reply(`This chat has ${count} members`); }); ``` ``` -------------------------------- ### Respond to Pre-Checkout Query and Handle Payment Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/query-methods.md This snippet demonstrates how to answer a pre-checkout query, confirming the final payment. It's crucial to respond within 10 seconds. The second part handles successful payment messages, fulfilling the order and replying to the user. ```typescript bot.on("pre_checkout_query", async (ctx) => { const query = ctx.preCheckoutQuery; // Verify order const order = await getOrder(query.invoice_payload); const amount = query.total_amount; if (order && order.price === amount) { // Everything is OK await ctx.preCheckoutQuery.answer(true); } else { // Problem with the order await ctx.preCheckoutQuery.answer(false, { error_message: "Order price mismatch or not found" }); } }); // After successful payment, handle the message update bot.on("message", async (ctx) => { if (ctx.message.successful_payment) { const payment = ctx.message.successful_payment; console.log(`Payment received: ${payment.total_amount} ${payment.currency}`); // Fulfill the order await fulfillOrder(payment.invoice_payload); await ctx.reply("Payment confirmed! Your order is being processed."); } }); ``` -------------------------------- ### getAdmins() Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/chat.md Get the list of administrators in the chat. This method returns an array of ChatMember objects, each with hydrated user methods. ```APIDOC ## getAdmins() ### Description Get the list of administrators in the chat. ### Signature ```typescript getAdmins( other?: Other<"getChatAdministrators">, signal?: AbortSignal, ): Promise ``` ### Parameters #### Path Parameters - **other** (Other<"getChatAdministrators">) - Optional - Additional parameters (rarely used) - **signal** (AbortSignal) - Optional - Optional signal to cancel the request ### Returns `Promise` - Array of ChatMember objects with hydrated user methods. ### Example ```typescript bot.on("message", async (ctx) => { const admins = await ctx.chat.getAdmins(); const names = admins .map(a => a.user.first_name) .join(", "); await ctx.reply(`Admins: ${names}`); }); ``` ``` -------------------------------- ### getMember() Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/chat.md Get information about a specific member of the chat. This method returns a ChatMember object with hydrated user methods. ```APIDOC ## getMember() ### Description Get information about a specific member of the chat. ### Signature ```typescript getMember( userId: number, other?: Other<"getChatMember">, signal?: AbortSignal, ): Promise ``` ### Parameters #### Path Parameters - **userId** (number) - Required - Unique identifier of the user - **other** (Other<"getChatMember">) - Optional - Additional parameters (rarely used) - **signal** (AbortSignal) - Optional - Optional signal to cancel the request ### Returns `Promise` - ChatMember object with hydrated user methods. ### Example ```typescript bot.on("message", async (ctx) => { const member = await ctx.chat.getMember(ctx.from.id); console.log(`Status: ${member.status}`); if (member.status === "creator") { await ctx.reply("Hello owner!"); } else if (member.status === "administrator") { await ctx.reply("Hello admin!"); } // Use hydrated user methods if (member.user) { const photos = await member.user.getProfilePhotos(); } }); ``` ``` -------------------------------- ### User Methods Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/INDEX.md Methods for managing user interactions within a chat, such as getting profile photos and moderating users. ```APIDOC ## User Methods ### Get user photos - `getProfilePhotos()` ### Ban from chat - `ban()` ### Unban from chat - `unban()` ### Limit permissions - `restrict()` ### Make administrator - `promote()` ### Set admin title - `setCustomTitle()` ``` -------------------------------- ### Inline Query with Various Result Types Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/inline-query.md Demonstrates how to use `ctx.inlineQuery.answer` with a diverse set of inline query result types, including articles, photos, videos, audio, documents, locations, venues, contacts, and games. ```typescript bot.on("inline_query", async (ctx) => { await ctx.inlineQuery.answer([ { type: "article", id: "article", title: "Text Result", input_message_content: { message_text: "Text content" } }, { type: "photo", id: "photo", photo_url: "https://example.com/photo.jpg", thumbnail_url: "https://example.com/thumb.jpg" }, { type: "video", id: "video", video_url: "https://example.com/video.mp4", mime_type: "video/mp4", thumbnail_url: "https://example.com/thumb.jpg", title: "Video Result" }, { type: "audio", id: "audio", audio_url: "https://example.com/audio.mp3", title: "Audio Result" }, { type: "document", id: "document", document_url: "https://example.com/doc.pdf", mime_type: "application/pdf", title: "Document Result" }, { type: "location", id: "location", latitude: 40.7128, longitude: -74.0060, title: "New York" }, { type: "venue", id: "venue", latitude: 40.7128, longitude: -74.0060, title: "Example Venue", address: "123 Main St, NY" }, { type: "contact", id: "contact", phone_number: "+1234567890", first_name: "John" }, { type: "game", id: "game", game_short_name: "my_game" } ]); }); ``` -------------------------------- ### Get Chat Menu Button Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/chat.md Retrieves the menu button configuration for a chat. Returns a `Promise` that resolves to the `MenuButton` object. ```typescript getMenuButton( other?: Other<"getChatMenuButton">, signal?: AbortSignal, ): Promise ``` -------------------------------- ### Get User Profile Photos Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/QUICK_REFERENCE.md Retrieve a user's profile photos. This operation may require specific permissions. ```typescript await user.getProfilePhotos(); ``` -------------------------------- ### Chat Member Operations Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/QUICK_REFERENCE.md Retrieve information about chat members, count members, get administrators, and set chat permissions. ```typescript // Members const member = await chat.getMember(userId); const count = await chat.getMembersCount(); const admins = await chat.getAdmins(); await chat.setPermissions({ can_send_messages: true }); ``` -------------------------------- ### PreCheckoutQueryXFragment Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/types.md Offers a method to answer pre-checkout queries, enabling bots to handle payment-related inquiries. ```APIDOC ## PreCheckoutQueryXFragment ### Description Offers a method to answer pre-checkout queries, enabling bots to handle payment-related inquiries. ### Methods - **answer**(`ok: boolean, other?: Other<"answerPreCheckoutQuery", "pre_checkout_query_id">, signal?: AbortSignal`): `Ret<"answerPreCheckoutQuery">` Answers a pre-checkout query, indicating success or failure. ``` -------------------------------- ### Moderation Workflow: Kick User Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/user.md Example of kicking a user and revoking their messages using the `ban` method within a message handler. ```typescript bot.on("message", async (ctx) => { const command = ctx.message.text?.split(" ") || []; if (command[0] === "/kick") { const userId = parseInt(command[1]); const user = await ctx.getChat(); // Get chat member info if (user) { await user.ban({ revoke_messages: true }); await ctx.reply(`User kicked and messages deleted`) } } if (command[0] === "/mute") { if (ctx.message.reply_to_message?.from) { const user = ctx.message.reply_to_message.from; // Need chat ID for restrict await ctx.api.restrictChatMember(ctx.chat.id, user.id, { can_send_messages: false }); } } }); ``` -------------------------------- ### Proxy-based Method Access (Alternative) Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/ARCHITECTURE.md Demonstrates an alternative approach using Proxies for method access, which was considered but not chosen for the hydration plugin. ```typescript // Not done - uses Object.assign instead return new Proxy(message, { get(target, prop) { if (prop === "delete") return () => api.deleteMessage(...); } }) ``` -------------------------------- ### Send Web App for Chat Join Request Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/query-methods.md Show a Mini App to the user before deciding on their join request. This is useful for verification or providing information. The bot must be an administrator with the 'can_invite_users' permission. An optional AbortSignal can be passed to cancel the request. ```typescript bot.on("chat_join_request", async (ctx) => { // Show verification web app await ctx.chatJoinRequest.sendWebApp( "https://example.com/verify?user_id=" + ctx.chatJoinRequest.from.id ); }); ``` -------------------------------- ### Adding a New Method to Messages Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/ARCHITECTURE.md Provides an example of how to extend the hydration plugin by adding a new method, `repoll`, to message objects. ```typescript // In src/data/message.ts export interface MessageXFragment { // Add new method signature repoll(signal?: AbortSignal): Ret<"someNewMethod">; } export function installMessageMethods(api: RawApi, message: Message) { const methods: MessageXFragment = { // Add implementation repoll: (signal) => api.someNewMethod({ ... }, signal), }; Object.assign(message, methods); } ``` -------------------------------- ### Main Plugin API Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/MANIFEST.md Documentation for the core `hydrate()` middleware function, `hydrateApi()` transformer, and `hydrateContext()` middleware, along with related types like `HydrateFlavor` and `HydrateApiFlavor`. ```APIDOC ## hydrate() Middleware ### Description Provides enhanced methods for message and chat interactions within your bot. ### Method `hydrate()` ### Parameters None ## hydrateApi() Transformer ### Description Transforms the API object to include hydrated methods. ### Method `hydrateApi()` ### Parameters None ## hydrateContext() Middleware ### Description Adds hydrated context to your bot's update handlers. ### Method `hydrateContext()` ### Parameters None ## HydrateFlavor ### Description A type that extends the context flavor with hydrate capabilities. ### Type `HydrateFlavor` ## HydrateApiFlavor ### Description A type that extends the API flavor with hydrate capabilities. ### Type `HydrateApiFlavor` ``` -------------------------------- ### Edited Message Handler Example Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/api-reference/update.md Shows how to handle edited messages and utilize the available message methods on the hydrated edited message object. ```typescript // Edited message bot.on("edited_message", async (ctx) => { // ctx.update.edited_message is MessageX const msg = ctx.editedMessage; if (msg) { await msg.delete(); } }); ``` -------------------------------- ### Handle Pre-Checkout Query Source: https://github.com/grammyjs/hydrate/blob/main/_autodocs/QUICK_REFERENCE.md Acknowledge a pre-checkout query to confirm the order details. This must be done within 10 seconds. ```typescript bot.on("pre_checkout_query", async (ctx) => { await ctx.preCheckoutQuery.answer(true); }); ```