Reputation: 817
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 |
Upvotes: 42
Views: 89388
Reputation: 367
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
Upvotes: 6
Reputation: 2305
I had to send tables in Telegram recently, and none of the formatting felt good enough. So I wrote a little function to convert a markdown table to a box-drawing table. It supports text alignment as well.
The code and demo are available on GitHub: box-table-drawing.
function convertMarkdownTableToBoxDrawing(markdownTable) {
const lines = markdownTable.trim().split('\n');
const headers = lines[0].split('|').map(header => header.trim()).filter(header => header);
const alignments = lines[1].split('|').map(header => header.trim()).filter(header => header);
const rows = lines.slice(2).map(line => line.split('|').map(cell => cell.trim()).filter(cell => cell));
const columnWidths = headers.map((header, i) => {
return Math.max(header.length, ...rows.map(row => row[i].length));
});
const drawLine = (char, junction, start, end) => {
return start + columnWidths.map(width => char.repeat(width + 2)).join(junction) + end;
};
const drawRow = (cells, left, middle, right, alignments) => {
return left + cells.map((cell, i) => {
const width = columnWidths[i];
if (alignments[i].startsWith(':') && alignments[i].endsWith(':')) {
return ' ' + cell.padStart((width + cell.length) / 2).padEnd(width) + ' ';
} else if (alignments[i].endsWith(':')) {
return ' ' + cell.padStart(width) + ' ';
} else {
return ' ' + cell.padEnd(width) + ' ';
}
}).join(middle) + right;
};
const topLine = drawLine('─', '┬', '┌', '┐');
const headerLine = drawRow(headers, '│', '│', '│', alignments);
const separatorLine = drawLine('─', '┼', '├', '┤');
const bottomLine = drawLine('─', '┴', '└', '┘');
const bodyLines = rows.map(row => drawRow(row, '│', '│', '│', alignments));
return [topLine, headerLine, separatorLine, ...bodyLines, bottomLine].join('\n');
}
Input:
| Quadrant | sin | cos | tan | cot |
|-----|:---:|:---:|:---:|:---:|
| 1 (0-90) | + | + | + | + |
| 2 (90-180) | + | - | - | - |
| 3 (180-270) | - | - | + | + |
| 4 (270-360) | - | + | - | - |
Output:
┌─────────────┬─────┬─────┬─────┬─────┐
│ Quadrant │ sin │ cos │ tan │ cot │
├─────────────┼─────┼─────┼─────┼─────┤
│ 1 (0-90) │ + │ + │ + │ + │
│ 2 (90-180) │ + │ - │ - │ - │
│ 3 (180-270) │ - │ - │ + │ + │
│ 4 (270-360) │ - │ + │ - │ - │
└─────────────┴─────┴─────┴─────┴─────┘
Just make sure to send it in the <pre>
tag with parse_mode: "HTML"
.
Upvotes: 1
Reputation: 11
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')
Upvotes: 1
Reputation: 613
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()
}
Well you just need to change the table headers and the rows of the table
Upvotes: 3
Reputation: 337
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);
}
Upvotes: 0
Reputation: 1527
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:
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 :
Upvotes: 27
Reputation: 360
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 |
+--------+-------+--------+
Upvotes: 17
Reputation: 401
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 |```
Upvotes: 16
Reputation: 817
I found this library - TableJs - that solves this problem. Works great on desktop clients however android clients didn't seem to render it properly.
Upvotes: 2
Reputation: 8015
You can use HTML or Markdown markup to send something like <pre>
in HTML. Just like this example.
Upvotes: 8