How do I send Tables with Telegram Bot API?
Asked Answered
G

10

39

I need to send stock market data and the formatting sucks right now. Need to send something liike this

| Symbol | Price | Change | 
|--------|-------|--------|
| ABC | 20.85 | 1.626 | 
| DEF | 78.95 | 0.099 | 
| GHI | 23.45 | 0.192 | 
| JKL | 98.85 | 0.292 |

This is what I have tried.

| Symbol | Price | Change |  
|--------|-------|--------|  
| ABC | 20.85 | 1.626 |   
| DEF | 78.95 | 0.099 |   
| GHI | 23.45 | 0.192 |    
| JKL | 98.85 | 0.292 |
Genie answered 18/3, 2018 at 8:34 Comment(0)
S
25

Set the Telegram API parse_mode parameter to HTML and wrap the message in <pre></pre> , but remember that telegram API does not support nested tags.

<pre>
| Tables   |      Are      |  Cool |
|----------|:-------------:|------:|
| col 1 is |  left-aligned | $1600 |
| col 2 is |    centered   |   $12 |
| col 3 is | right-aligned |    $1 |
</pre>

Result in Telegram messanger:

ScreenShot from telegram bot

Updated. How convert the tables in the picture

There will be a problem on the small screens of smartphones. So this method is not good. The only option is to convert the tables in the picture and so send :

  1. Or you can convert HTML to image using a headerless browser on your server.
  2. Or you can convert HTML to image using here external API services
  3. Or you can convert HTML to image using more difficult way by php GD
Sexpartite answered 21/5, 2019 at 12:50 Comment(3)
How to convert table into picture ?Quadrant
how can I convert pandas dataframe to this html format? I tried df.to_html() but that didn work.Daubery
Kundan, I don't know pandas dataframe for Python. But I know that too complicated convert any HTML to rich txt format for Telegram message. You should use Telegram service for instance view HTML articles telegram.org/blog/instant-view in your projectSexpartite
R
16

Try this

```| Symbol | Price | Change |
|--------|-------|--------|
| ABC    | 20.85 |  1.626 |
| DEF    | 78.95 |  0.099 |
| GHI    | 23.45 |  0.192 |
| JKL    | 98.85 |  0.292 |```
Revive answered 15/1, 2020 at 10:3 Comment(1)
You need to set parse_mode=ParseMode.MARKDOWN_V2 parameter , for this to work.Chansoo
M
16

Import "prettytable" library in python to format your table:

import prettytable as pt
from telegram import ParseMode
from telegram.ext import CallbackContext, Updater


def send_table(update: Updater, context: CallbackContext):
    table = pt.PrettyTable(['Symbol', 'Price', 'Change'])
    table.align['Symbol'] = 'l'
    table.align['Price'] = 'r'
    table.align['Change'] = 'r'

    data = [
        ('ABC', 20.85, 1.626),
        ('DEF', 78.95, 0.099),
        ('GHI', 23.45, 0.192),
        ('JKL', 98.85, 0.292),
    ]
    for symbol, price, change in data:
        table.add_row([symbol, f'{price:.2f}', f'{change:.3f}'])

    update.message.reply_text(f'<pre>{table}</pre>', parse_mode=ParseMode.HTML)
    # or use markdown
    update.message.reply_text(f'```{table}```', parse_mode=ParseMode.MARKDOWN_V2)

You will receive message like:

+--------+-------+--------+
| Symbol | Price | Change |
+--------+-------+--------+
| ABC    | 20.85 |  1.626 |
| DEF    | 78.95 |  0.099 |
| GHI    | 23.45 |  0.192 |
| JKL    | 98.85 |  0.292 |
+--------+-------+--------+
Muttonhead answered 16/4, 2021 at 7:35 Comment(2)
good solution! FYI, it plays bad with emojis in column namesMccollough
@Mccollough Do you mean this? github.com/jazzband/prettytable/pull/56Coleman
Q
8

You can use HTML or Markdown markup to send something like <pre> in HTML. Just like this example.

Quotha answered 18/3, 2018 at 9:6 Comment(1)
or he can convert the table to an image.Unwholesome
S
6

The easiest and most professional method is to use Telegram MiniApp, which was added in the recent update.

step 1: create html file and write your table.

step2: add this script to your html file. <script src="https://telegram.org/js/telegram-web-app.js"></script>

step 3: redirect user to page with this method of api

{
    "text": "Test web_app",
    "web_app": {
        "url": "https://yourDomain/yourFile.html"
    }
}

note: page will show in bot page not browser

for more info read official document: https://core.telegram.org/bots/webapps#initializing-web-apps

Seligmann answered 5/1, 2023 at 10:13 Comment(0)
B
4

Here is my solution using puppeteer to screenshot on the table element

First of all you need to generate the table HTML code here is the code to generate that code

async function generateHtml(rows) {
    return `<!DOCTYPE html>
    <html>
    <head>
    <style>
    thead,
tfoot {
    background-color: #3f87a6;
    color: #fff;
}

tbody {
    background-color: #e4f0f5;
}

caption {
    padding: 10px;
    caption-side: bottom;
}

table {
    border-collapse: collapse;
    border: 2px solid rgb(200, 200, 200);
    letter-spacing: 1px;
    font-family: sans-serif;
    font-size: .8rem;
}

td,
th {
    border: 1px solid rgb(190, 190, 190);
    padding: 5px 10px;
}

td {
    text-align: center;
}

    </style>
    </head>
    <body>
    <table>
    <caption>Pornhub Pages Summary</caption>
    <thead>
        <tr>
            <th>ID</th>
            <th scope="col">Progress</th>
            <th scope="col">Stucked</th>
            <th scope="col">Finished</th>
            <th scope="col">Busy</th>
        </tr>
    </thead>
    <tbody>
        ${rows}
    </tbody>
</table>
    </body>
    </html>`
}

And here is the code for generate the rows argument of the above function

async function getTheImgOfTheSummaryOfThePages() {
    const rows = []
    for (const [index, val] of statuesOfThePages.entries()) {
        const row = `<tr>
        <th scope="row">${index}</th>
        <th>${val.progress}</th>
        <th>${val.stucked}</th>
        <th>${val.finished}</th>
        <th>${val.pageBusy}</th>
      </tr>`

        rows.push(row)
    }

    const path = './summaryOfThePagesOfPornhub.png'
    const html = await generateHtml(rows.join('\n'))
    await util.takescrrenshotOnTheHtml(html, browser, path, 'table')
    return path
}

And here is the code for screenshot on the table element

async function takescrrenshotOnTheHtml(html, browser, pathToSave, onElement) {
    const page = await newPage(browser);
    await page.setContent(html)
    const element = await page.$(onElement)
    await element.screenshot({path: pathToSave})
    await page.close()
}

Here is the result enter image description here

Well you just need to change the table headers and the rows of the table

Buckjump answered 20/9, 2022 at 2:16 Comment(0)
G
2

I found this library - TableJs - that solves this problem. Works great on desktop clients however android clients didn't seem to render it properly.

Genie answered 3/9, 2019 at 3:7 Comment(1)
I had the same problem on Android, but fixed it by overriding the config.border property and using a more basic character set.Perdure
B
1

Formatting the text as "Monospace" works too

Bleb answered 20/6, 2020 at 6:58 Comment(0)
N
1
import warnings
from PIL import Image, ImageDraw, ImageFont

def table_to_image(my_table):
    warnings.filterwarnings('ignore', category=DeprecationWarning)
    font = ImageFont.truetype("courbd.ttf", 15)
    text_width, text_height = font.getsize_multiline(my_table.get_string())
    im = Image.new("RGB", (text_width + 15, text_height + 15), "white")
    draw = ImageDraw.Draw(im)
    draw.text((7, 7), my_table.get_string(), font=font, fill="black")
    im.show()
    im.save(my_table_image.png, 'PNG')
Neruda answered 21/11, 2022 at 12:27 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Deicer
T
0

I wrote a code to build a Telegram html table from an array of strings.

Just build an array with the lines with columns data separated by ";" and this code will output the Telegram ready table.

Enjoy, figure out the parameters :)

You must use "parse_mode" = "html" when sending the message to Telegram Api.

public string BuildTelegramTable(
            List<string> table_lines,
            string tableColumnSeparator = "|", char inputArraySeparator = ';',
            int maxColumnWidth = 0, bool fixedColumnWidth = false, bool autoColumnWidth = false,
            int minimumColumnWidth = 4, int columnPadRight = 0, int columnPadLeft = 0,
            bool beginEndBorders = true)
        {
            var prereadyTable = new List<string>() { "<pre>" };
            var columnsWidth = new List<int>();
            var firstLine = table_lines[0];
            var lineVector = firstLine.Split(inputArraySeparator);

            if (fixedColumnWidth && maxColumnWidth == 0) throw new ArgumentException("For fixedColumnWidth usage must set maxColumnWidth > 0");
            else if (fixedColumnWidth && maxColumnWidth > 0)
            {
                for(var x=0;x<lineVector.Length;x++)
                    columnsWidth.Add(maxColumnWidth + columnPadRight + columnPadLeft);
            }
            else
            {
                for(var x=0;x<lineVector.Length;x++)
                {
                    var columnData = lineVector[x].Trim();
                    var columnFullLength = columnData.Length;

                    if (autoColumnWidth)
                        table_lines.ForEach(line => columnFullLength = line.Split(inputArraySeparator)[x].Length > columnFullLength ? line.Split(inputArraySeparator)[x].Length : columnFullLength);
                    
                    columnFullLength = columnFullLength < minimumColumnWidth ? minimumColumnWidth : columnFullLength;

                    var columnWidth = columnFullLength + columnPadRight + columnPadLeft;

                    if (maxColumnWidth > 0 && columnWidth > maxColumnWidth)
                        columnWidth = maxColumnWidth;

                    columnsWidth.Add(columnWidth);
                }
            }

            foreach(var line in table_lines)
            {
                lineVector = line.Split(inputArraySeparator);

                var fullLine = new string[lineVector.Length+(beginEndBorders ? 2 : 0)];
                if (beginEndBorders) fullLine[0] = "";

                for(var x=0;x<lineVector.Length;x++)
                {
                    var clearedData = lineVector[x].Trim();
                    var dataLength = clearedData.Length;
                    var columnWidth = columnsWidth[x];
                    var columnSizeWithoutTrimSize = columnWidth - columnPadRight - columnPadLeft;
                    var dataCharsToRead = columnSizeWithoutTrimSize > dataLength ? dataLength : columnSizeWithoutTrimSize;
                    var columnData = clearedData.Substring(0,dataCharsToRead);
                    columnData = columnData.PadRight(columnData.Length + columnPadRight);
                    columnData = columnData.PadLeft(columnData.Length + columnPadLeft);

                    var column = columnData.PadRight(columnWidth);

                    fullLine[x+(beginEndBorders ? 1 : 0)] = column;
                }

                if (beginEndBorders) fullLine[fullLine.Length - 1] = "";

                prereadyTable.Add(string.Join(tableColumnSeparator,fullLine));
            }

            prereadyTable.Add("</pre>");

            return string.Join("\r\n",prereadyTable);
        }
Trilbi answered 31/5, 2022 at 18:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.