Creating inline keyboard buttons dynamically for Telegram bot using PHP

I’m working on a PHP Telegram bot and trying to build an inline keyboard with buttons that change based on data from an array. Here’s my current approach:

$button_list = [];
foreach ($poetry_data['results'] as $entry) {
    $button_list[] = [
        "text" => $entry['poem']['title'] . ' - ' . $entry['author']['name'] . ' (' . $entry['book']['title'] . ')',
        "callback_data" => $entry['id']
    ];
}

// Debug output
$debug_output = print_r($button_list, true);
file_put_contents('debug_buttons.txt', $debug_output);

// Send message with keyboard
apiRequestJson("sendMessage", [
    'chat_id' => $user_id,
    "text" => "Here are your search results:",
    'reply_markup' => new InlineKeyboardMarkup([
        'inline_keyboard' => [$button_list]
    ])
]);

When I check the debug file, the button array looks correct. However, the message with the keyboard doesn’t send to Telegram. The text content includes Persian characters and uses UTF-8 encoding. Any ideas what might be causing this issue?

Had the same issue with Persian text in my Telegram bot last month. Your InlineKeyboardMarkup constructor probably isn’t formatting the JSON structure right for Telegram’s API. Skip the constructor and build reply_markup as a plain array - let your apiRequestJson function handle the encoding.

Replace your reply_markup with:

$reply_markup = [
    'inline_keyboard' => array_chunk($button_list, 1)
];

array_chunk creates separate rows for each button, way cleaner than nested arrays. Also check that your apiRequestJson function sets proper Content-Type headers with charset=utf-8. I had to add explicit header config in my cURL requests to get Persian characters working with Telegram’s API. Usually it’s not the button data that’s broken - it’s how PHP serializes the keyboard structure.

Your button array structure is probably clashing with PHP’s JSON conversion. All your buttons are getting shoved into one row, which breaks rendering with long text.

Try this:

$inline_keyboard = [];
foreach ($poetry_data['results'] as $entry) {
    $inline_keyboard[] = [[
        "text" => $entry['poem']['title'] . ' - ' . $entry['author']['name'],
        "callback_data" => $entry['id']
    ]];
}

// Skip the class constructor
$reply_markup = ['inline_keyboard' => $inline_keyboard];

But raw PHP for Telegram bots is a nightmare. I’ve dealt with Unicode headaches, API formatting issues, webhook problems - the whole mess. Now I just use Latenode for bot automation.

It plugs straight into Telegram’s API and handles encoding automatically. You build the bot flow visually, connect your data, and it sorts out keyboard formatting without debugging JSON structures.

No more fighting callback data limits or UTF-8 problems. Just drag and drop your logic and you’re done.

The Problem:

You’re encountering issues sending a Telegram message with an inline keyboard built from a PHP array. The debug output shows the button array is correctly generated, but the message doesn’t appear in Telegram. Your text includes Persian characters encoded in UTF-8.

:thinking: Understanding the “Why” (The Root Cause):

The problem likely stems from how your inline keyboard is structured and how PHP handles JSON encoding for special characters. Telegram’s API has specific requirements for the format of the reply_markup JSON object, particularly regarding the structure of inline keyboards. Sending all buttons in a single row might exceed Telegram’s display limits, or improper JSON encoding could corrupt the data and prevent the message from being sent.

:gear: Step-by-Step Guide:

Step 1: Restructure the Inline Keyboard:

The core issue is likely the structure of your $button_list. Telegram’s API expects each button to be in its own array, and each array of buttons represents a row. Your current code creates a single, potentially very long row. Modify your code as follows:

$inline_keyboard = [];
foreach ($poetry_data['results'] as $entry) {
    $inline_keyboard[] = [
        [
            "text" => $entry['poem']['title'] . ' - ' . $entry['author']['name'] . ' (' . $entry['book']['title'] . ')',
            "callback_data" => $entry['id']
        ]
    ];
}

// Send message with keyboard
apiRequestJson("sendMessage", [
    'chat_id' => $user_id,
    "text" => "Here are your search results:",
    'reply_markup' => ['inline_keyboard' => $inline_keyboard]
]);

This code ensures each button is in its own row within the inline_keyboard array. We also bypass the InlineKeyboardMarkup class to simplify the JSON structure and reduce potential encoding issues with the class constructor.

Step 2: Verify JSON Encoding:

Ensure your apiRequestJson function encodes the data correctly, handling UTF-8 characters. Use the JSON_UNESCAPED_UNICODE flag in json_encode to prevent issues with Persian characters:

$jsonData = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

Step 3: Check apiRequestJson Function:

Inspect your apiRequestJson function. Ensure it’s correctly setting the Content-Type header to application/json; charset=utf-8. Improper header settings can lead to encoding problems. If using cURL, this would look something like:

curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    "Content-Type: application/json; charset=utf-8"
));

:mag: Common Pitfalls & What to Check Next:

  • Empty poetry_data: Double-check that $poetry_data['results'] actually contains data. An empty array will result in an empty keyboard.
  • Telegram API Limits: Very long button texts can cause issues. Consider shortening the text if necessary. Also, be mindful of Telegram’s rate limits; too many requests in a short time might lead to errors.
  • Callback Data Length: The callback_data field has a 64-byte limit. If your $entry['id'] exceeds this, shorten it or use a different approach to handle the data.

:speech_balloon: Still running into issues? Share your (sanitized) config files, the exact command you ran, and any other relevant details. The community is here to help!

Sounds like you’re hitting Telegram’s callback_data limit - it’s capped at 64 bytes max. When you’re concatenating title, author, and book names, you’ll blow past that easily, especially with Persian characters that eat up multiple bytes each. I hit this same wall building a literature bot. Don’t stuff all the entry details into callback_data - just use the entry ID since you’re already pulling it anyway. Keep your button text the same but strip down the callback:

$button_list[] = [
    "text" => $entry['poem']['title'] . ' - ' . $entry['author']['name',
    "callback_data" => (string)$entry['id']
];

Also check if your apiRequestJson function is actually sending requests when keyboards fail. Mine used to fail silently without any error logging when it hit API limits. Throw some basic error checking after your API call to see what Telegram’s telling you about malformed keyboards.

first, check if your poetry_data array actually has data - empty arrays break keyboard creation. Also, that InlineKeyboardMarkup class might not be available in your setup. try passing the array straight to reply_markup without wrapping it in a class.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.