kformat-telebot 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
@@ -0,0 +1,17 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 KFormat contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.5
2
+ Name: kformat-telebot
3
+ Version: 0.1.0
4
+ Summary: Short Telegram HTML formatting, tables, inline keyboards, and Rich Message JSON helpers for pyTelegramBotAPI.
5
+ Project-URL: Homepage, https://pypi.org/project/kformat-telebot/
6
+ Project-URL: Repository, https://github.com/YOUR_USERNAME/kformat-telebot
7
+ Author: KFormat contributors
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: bot,formatting,pytelegrambotapi,rich-message,telebot,telegram
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Communications :: Chat
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.8
24
+ Provides-Extra: dev
25
+ Requires-Dist: build>=1.2; extra == 'dev'
26
+ Requires-Dist: twine>=5.1; extra == 'dev'
27
+ Provides-Extra: telebot
28
+ Requires-Dist: pytelegrambotapi>=4.14; extra == 'telebot'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # KFormat
32
+
33
+ Short aliases for Telegram HTML formatting, tables, inline keyboards, and Rich Message JSON helpers.
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install kformat-telebot
39
+ ```
40
+
41
+ For inline keyboard helpers, install the optional TeleBot dependency:
42
+
43
+ ```bash
44
+ pip install "kformat-telebot[telebot]"
45
+ ```
46
+
47
+ ## Quick example
48
+
49
+ ```python
50
+ import telebot
51
+ from kformat import K
52
+
53
+ bot = telebot.TeleBot("YOUR_BOT_TOKEN")
54
+
55
+ @bot.message_handler(commands=["start"])
56
+ def start(message):
57
+ text = (
58
+ K.b("Bold") + "\n"
59
+ + K.un("Underline") + "\n"
60
+ + K.sp("Spoiler") + "\n"
61
+ + K.a("Open website", "https://example.com")
62
+ )
63
+ bot.send_message(message.chat.id, text, parse_mode="HTML")
64
+
65
+ bot.infinity_polling()
66
+ ```
67
+
68
+ ## Tables
69
+
70
+ ```python
71
+ text = K.table(
72
+ [["Kentucky", "10"], ["Kentucky", "20"]],
73
+ headers=["Name", "Count"]
74
+ )
75
+ bot.send_message(chat_id, text, parse_mode="HTML")
76
+ ```
77
+
78
+ Classic Telegram `sendMessage` has no native table-cell layout, so `K.table` renders
79
+ a readable monospace text table.
80
+
81
+ ## Inline keyboard
82
+
83
+ ```python
84
+ markup = K.buttons(
85
+ [("Open", "callback:open"), ("Website", "url:https://example.com")],
86
+ [K.btn("Second row", data="second")]
87
+ )
88
+ bot.send_message(chat_id, "Choose:", reply_markup=markup)
89
+ ```
90
+
91
+ ## Rich Message JSON
92
+
93
+ ```python
94
+ payload = K.rich(
95
+ K.rh("Heading"),
96
+ K.rb("Paragraph"),
97
+ K.rtable([["Name", "Count"], ["Kentucky", "10"]])
98
+ )
99
+ print(K.rich_json(K.rh("Heading"), K.rb("Paragraph")))
100
+ ```
101
+
102
+ Rich Message helpers only build the JSON structure. Sending it requires a Telegram
103
+ Bot API method/client that supports Rich Messages.
104
+
105
+ ## API
106
+
107
+ - `K.b(text)`: bold
108
+ - `K.i(text)`: italic
109
+ - `K.un(text)`: underline
110
+ - `K.st(text)`: strikethrough
111
+ - `K.sp(text)`: spoiler
112
+ - `K.c(text)`: inline code
113
+ - `K.pre(text, lang=None)`: preformatted code
114
+ - `K.a(text, url)`: hyperlink
115
+ - `K.tg(text, user_id)`: user mention
116
+ - `K.block(text)`: blockquote
117
+ - `K.expand(text)`: expandable blockquote
118
+ - `K.table(rows, headers=None)`: monospace table
119
+ - `K.buttons(*rows)`: inline keyboard markup
120
+ - `K.btn(text, data=... or url=...)`: button specification
121
+ - `K.rb`, `K.rh`, `K.rdiv`, `K.rtable`, `K.rbutton`, `K.rbuttons`, `K.rich`, `K.rich_json`: Rich Message JSON builders
122
+ - `K.ul`, `K.ol`, `K.bullet`, `K.hr`, `K.join`, `K.plain`, `K.raw`
123
+ - `K.help()`: usage guide
124
+
125
+ Text helpers return Telegram HTML fragments. Use `parse_mode="HTML"`.
126
+ Text is escaped automatically. `K.raw()` is for trusted HTML only.
@@ -0,0 +1,96 @@
1
+ # KFormat
2
+
3
+ Short aliases for Telegram HTML formatting, tables, inline keyboards, and Rich Message JSON helpers.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install kformat-telebot
9
+ ```
10
+
11
+ For inline keyboard helpers, install the optional TeleBot dependency:
12
+
13
+ ```bash
14
+ pip install "kformat-telebot[telebot]"
15
+ ```
16
+
17
+ ## Quick example
18
+
19
+ ```python
20
+ import telebot
21
+ from kformat import K
22
+
23
+ bot = telebot.TeleBot("YOUR_BOT_TOKEN")
24
+
25
+ @bot.message_handler(commands=["start"])
26
+ def start(message):
27
+ text = (
28
+ K.b("Bold") + "\n"
29
+ + K.un("Underline") + "\n"
30
+ + K.sp("Spoiler") + "\n"
31
+ + K.a("Open website", "https://example.com")
32
+ )
33
+ bot.send_message(message.chat.id, text, parse_mode="HTML")
34
+
35
+ bot.infinity_polling()
36
+ ```
37
+
38
+ ## Tables
39
+
40
+ ```python
41
+ text = K.table(
42
+ [["Kentucky", "10"], ["Kentucky", "20"]],
43
+ headers=["Name", "Count"]
44
+ )
45
+ bot.send_message(chat_id, text, parse_mode="HTML")
46
+ ```
47
+
48
+ Classic Telegram `sendMessage` has no native table-cell layout, so `K.table` renders
49
+ a readable monospace text table.
50
+
51
+ ## Inline keyboard
52
+
53
+ ```python
54
+ markup = K.buttons(
55
+ [("Open", "callback:open"), ("Website", "url:https://example.com")],
56
+ [K.btn("Second row", data="second")]
57
+ )
58
+ bot.send_message(chat_id, "Choose:", reply_markup=markup)
59
+ ```
60
+
61
+ ## Rich Message JSON
62
+
63
+ ```python
64
+ payload = K.rich(
65
+ K.rh("Heading"),
66
+ K.rb("Paragraph"),
67
+ K.rtable([["Name", "Count"], ["Kentucky", "10"]])
68
+ )
69
+ print(K.rich_json(K.rh("Heading"), K.rb("Paragraph")))
70
+ ```
71
+
72
+ Rich Message helpers only build the JSON structure. Sending it requires a Telegram
73
+ Bot API method/client that supports Rich Messages.
74
+
75
+ ## API
76
+
77
+ - `K.b(text)`: bold
78
+ - `K.i(text)`: italic
79
+ - `K.un(text)`: underline
80
+ - `K.st(text)`: strikethrough
81
+ - `K.sp(text)`: spoiler
82
+ - `K.c(text)`: inline code
83
+ - `K.pre(text, lang=None)`: preformatted code
84
+ - `K.a(text, url)`: hyperlink
85
+ - `K.tg(text, user_id)`: user mention
86
+ - `K.block(text)`: blockquote
87
+ - `K.expand(text)`: expandable blockquote
88
+ - `K.table(rows, headers=None)`: monospace table
89
+ - `K.buttons(*rows)`: inline keyboard markup
90
+ - `K.btn(text, data=... or url=...)`: button specification
91
+ - `K.rb`, `K.rh`, `K.rdiv`, `K.rtable`, `K.rbutton`, `K.rbuttons`, `K.rich`, `K.rich_json`: Rich Message JSON builders
92
+ - `K.ul`, `K.ol`, `K.bullet`, `K.hr`, `K.join`, `K.plain`, `K.raw`
93
+ - `K.help()`: usage guide
94
+
95
+ Text helpers return Telegram HTML fragments. Use `parse_mode="HTML"`.
96
+ Text is escaped automatically. `K.raw()` is for trusted HTML only.
@@ -0,0 +1,144 @@
1
+ """KFormat: short Telegram HTML and keyboard helpers for pyTelegramBotAPI."""
2
+ import html, json, re
3
+
4
+ def _e(x):
5
+ return html.escape(str(x), quote=False)
6
+
7
+ class K:
8
+ @staticmethod
9
+ def b(x): return f"<b>{_e(x)}</b>"
10
+ @staticmethod
11
+ def i(x): return f"<i>{_e(x)}</i>"
12
+ @staticmethod
13
+ def un(x): return f"<u>{_e(x)}</u>"
14
+ @staticmethod
15
+ def st(x): return f"<s>{_e(x)}</s>"
16
+ @staticmethod
17
+ def sp(x): return f"<tg-spoiler>{_e(x)}</tg-spoiler>"
18
+ @staticmethod
19
+ def c(x): return f"<code>{_e(x)}</code>"
20
+ @staticmethod
21
+ def pre(x, lang=None):
22
+ body = _e(x)
23
+ if lang:
24
+ lang = re.sub(r"[^a-zA-Z0-9_+#.-]", "", str(lang))
25
+ return f'<pre><code class="language-{lang}">{body}</code></pre>'
26
+ return f"<pre>{body}</pre>"
27
+ @staticmethod
28
+ def a(text, url): return f'<a href="{html.escape(str(url), quote=True)}">{_e(text)}</a>'
29
+ @staticmethod
30
+ def tg(text, user_id): return f'<a href="tg://user?id={int(user_id)}">{_e(text)}</a>'
31
+ @staticmethod
32
+ def block(x): return f"<blockquote>{_e(x)}</blockquote>"
33
+ @staticmethod
34
+ def expand(x): return f"<blockquote expandable>{_e(x)}</blockquote>"
35
+ @staticmethod
36
+ def raw(x): return str(x) # trusted HTML only
37
+ @staticmethod
38
+ def plain(x): return _e(x)
39
+ @staticmethod
40
+ def ul(*items): return "\n".join("• " + _e(x) for x in items)
41
+ @staticmethod
42
+ def ol(*items): return "\n".join(f"{n}. " + _e(x) for n, x in enumerate(items, 1))
43
+ @staticmethod
44
+ def bullet(x): return "• " + _e(x)
45
+ @staticmethod
46
+ def hr(char="─", count=18): return _e((str(char) or "─")[0] * max(1, min(int(count), 100)))
47
+ @staticmethod
48
+ def join(*parts, sep=""): return sep.join(map(str, parts))
49
+ @staticmethod
50
+ def table(rows, headers=None, gap=2):
51
+ data = ([list(headers)] if headers is not None else []) + [list(r) for r in rows]
52
+ if not data: return "<pre></pre>"
53
+ n = max(map(len, data))
54
+ data = [[str(v).replace("\n", " ") for v in r] + [""]*(n-len(r)) for r in data]
55
+ widths = [max(len(r[i]) for r in data) for i in range(n)]
56
+ lines = []
57
+ for ri, row in enumerate(data):
58
+ lines.append((" "*gap).join(row[i].ljust(widths[i]) for i in range(n)).rstrip())
59
+ if headers is not None and ri == 0:
60
+ lines.append((" "*gap).join("-"*w for w in widths).rstrip())
61
+ return "<pre>" + _e("\n".join(lines)) + "</pre>"
62
+ @staticmethod
63
+ def btn(text, data=None, url=None):
64
+ if (data is None) == (url is None): raise ValueError("حدد data أو url فقط")
65
+ return {"text": str(text), "callback_data": str(data)} if data is not None else {"text": str(text), "url": str(url)}
66
+ @staticmethod
67
+ def buttons(*rows):
68
+ try:
69
+ from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
70
+ except ImportError as e:
71
+ raise RuntimeError("ثبت المكتبة: pip install pyTelegramBotAPI") from e
72
+ markup = InlineKeyboardMarkup()
73
+ for row in rows:
74
+ if not isinstance(row, (list, tuple)): row = [row]
75
+ built = []
76
+ for item in row:
77
+ if isinstance(item, dict):
78
+ spec = dict(item); label = spec.pop("text", spec.pop("label", "Button"))
79
+ built.append(InlineKeyboardButton(str(label), **spec))
80
+ elif isinstance(item, str):
81
+ built.append(InlineKeyboardButton(item, callback_data=item))
82
+ elif isinstance(item, (list, tuple)) and len(item) >= 2:
83
+ label, target = str(item[0]), str(item[1])
84
+ if target.startswith("url:"): built.append(InlineKeyboardButton(label, url=target[4:]))
85
+ elif target.startswith("webapp:"): built.append(InlineKeyboardButton(label, web_app=WebAppInfo(target[7:])))
86
+ else: built.append(InlineKeyboardButton(label, callback_data=target[9:] if target.startswith("callback:") else target))
87
+ else: raise ValueError("صيغة زر غير صحيحة")
88
+ if built: markup.row(*built)
89
+ return markup
90
+ # Builders for Telegram Rich Message JSON. These build data; they do not send it.
91
+ @staticmethod
92
+ def rb(text): return {"type":"paragraph", "text":str(text)}
93
+ @staticmethod
94
+ def rh(text, size=1): return {"type":"heading", "text":str(text), "size":max(1,min(6,int(size)))}
95
+ @staticmethod
96
+ def rdiv(): return {"type":"divider"}
97
+ @staticmethod
98
+ def rtable(rows, header=True, bordered=True, striped=False, compact=False):
99
+ cells = [[{"text":str(v), **({"is_header":True} if header and ri==0 else {})} for v in row] for ri,row in enumerate(rows)]
100
+ return {"type":"table","cells":cells,"is_bordered":bool(bordered),"is_striped":bool(striped),"is_compact":bool(compact)}
101
+ @staticmethod
102
+ def rbutton(text, **action):
103
+ if len(action)!=1: raise ValueError("زر Rich يحتاج إجراء واحد")
104
+ return {"text":str(text), **action}
105
+ @staticmethod
106
+ def rbuttons(*rows, align="center"):
107
+ return {"type":"buttons","buttons":[dict(x) for row in rows for x in row],"align":align if align in ("left","center","right") else "center"}
108
+ @staticmethod
109
+ def rich(*blocks, is_rtl=True): return {"is_rtl":bool(is_rtl),"blocks":list(blocks)}
110
+ @staticmethod
111
+ def rich_json(*blocks, is_rtl=True, indent=2):
112
+ return json.dumps({"rich_message":K.rich(*blocks,is_rtl=is_rtl)},ensure_ascii=False,indent=indent)
113
+ @staticmethod
114
+ def help():
115
+ return """KFormat guide
116
+ b(text) عريض
117
+ i(text) مائل
118
+ un(text) تسطير
119
+ st(text) مشطوب
120
+ sp(text) نص مخفي
121
+ c(text) كود داخل السطر
122
+ pre(text,lang) كتلة كود
123
+ a(text,url) رابط
124
+ tg(text,id) منشن بالآيدي
125
+ block(text) اقتباس
126
+ expand(text) اقتباس قابل للطي
127
+ table(rows, headers=[]) جدول نصي
128
+ buttons(*rows) أزرار Inline
129
+ btn(text,data=...) أو btn(text,url=...)
130
+ ul(*items), ol(*items), bullet(text), hr()
131
+ rb/rh/rdiv/rtable/rbutton/rbuttons/rich/rich_json لبناء Rich Message JSON
132
+
133
+ دوال النص تحتاج parse_mode='HTML'.
134
+ الجدول العادي نص monospace مرتب، مو خلايا أصلية في sendMessage.
135
+ دوال Rich تبني JSON فقط، ولا ترسله وحدها.
136
+ """
137
+ bold=K.b
138
+ italic=K.i
139
+ underline=K.un
140
+ strike=K.st
141
+ spoiler=K.sp
142
+ code=K.c
143
+ table=K.table
144
+ buttons=K.buttons
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.26"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "kformat-telebot"
7
+ version = "0.1.0"
8
+ description = "Short Telegram HTML formatting, tables, inline keyboards, and Rich Message JSON helpers for pyTelegramBotAPI."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "KFormat contributors" }]
13
+ keywords = ["telegram", "telebot", "pytelegrambotapi", "formatting", "bot", "rich-message"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.8",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Communications :: Chat",
26
+ "Topic :: Software Development :: Libraries :: Python Modules"
27
+ ]
28
+ dependencies = []
29
+
30
+ [project.optional-dependencies]
31
+ telebot = ["pyTelegramBotAPI>=4.14"]
32
+ dev = ["build>=1.2", "twine>=5.1"]
33
+
34
+ [project.urls]
35
+ Homepage = "https://pypi.org/project/kformat-telebot/"
36
+ Repository = "https://github.com/YOUR_USERNAME/kformat-telebot"
37
+
38
+ [tool.hatch.build.targets.wheel]
39
+ packages = ["src/kformat"]
@@ -0,0 +1,144 @@
1
+ """KFormat: short Telegram HTML and keyboard helpers for pyTelegramBotAPI."""
2
+ import html, json, re
3
+
4
+ def _e(x):
5
+ return html.escape(str(x), quote=False)
6
+
7
+ class K:
8
+ @staticmethod
9
+ def b(x): return f"<b>{_e(x)}</b>"
10
+ @staticmethod
11
+ def i(x): return f"<i>{_e(x)}</i>"
12
+ @staticmethod
13
+ def un(x): return f"<u>{_e(x)}</u>"
14
+ @staticmethod
15
+ def st(x): return f"<s>{_e(x)}</s>"
16
+ @staticmethod
17
+ def sp(x): return f"<tg-spoiler>{_e(x)}</tg-spoiler>"
18
+ @staticmethod
19
+ def c(x): return f"<code>{_e(x)}</code>"
20
+ @staticmethod
21
+ def pre(x, lang=None):
22
+ body = _e(x)
23
+ if lang:
24
+ lang = re.sub(r"[^a-zA-Z0-9_+#.-]", "", str(lang))
25
+ return f'<pre><code class="language-{lang}">{body}</code></pre>'
26
+ return f"<pre>{body}</pre>"
27
+ @staticmethod
28
+ def a(text, url): return f'<a href="{html.escape(str(url), quote=True)}">{_e(text)}</a>'
29
+ @staticmethod
30
+ def tg(text, user_id): return f'<a href="tg://user?id={int(user_id)}">{_e(text)}</a>'
31
+ @staticmethod
32
+ def block(x): return f"<blockquote>{_e(x)}</blockquote>"
33
+ @staticmethod
34
+ def expand(x): return f"<blockquote expandable>{_e(x)}</blockquote>"
35
+ @staticmethod
36
+ def raw(x): return str(x) # trusted HTML only
37
+ @staticmethod
38
+ def plain(x): return _e(x)
39
+ @staticmethod
40
+ def ul(*items): return "\n".join("• " + _e(x) for x in items)
41
+ @staticmethod
42
+ def ol(*items): return "\n".join(f"{n}. " + _e(x) for n, x in enumerate(items, 1))
43
+ @staticmethod
44
+ def bullet(x): return "• " + _e(x)
45
+ @staticmethod
46
+ def hr(char="─", count=18): return _e((str(char) or "─")[0] * max(1, min(int(count), 100)))
47
+ @staticmethod
48
+ def join(*parts, sep=""): return sep.join(map(str, parts))
49
+ @staticmethod
50
+ def table(rows, headers=None, gap=2):
51
+ data = ([list(headers)] if headers is not None else []) + [list(r) for r in rows]
52
+ if not data: return "<pre></pre>"
53
+ n = max(map(len, data))
54
+ data = [[str(v).replace("\n", " ") for v in r] + [""]*(n-len(r)) for r in data]
55
+ widths = [max(len(r[i]) for r in data) for i in range(n)]
56
+ lines = []
57
+ for ri, row in enumerate(data):
58
+ lines.append((" "*gap).join(row[i].ljust(widths[i]) for i in range(n)).rstrip())
59
+ if headers is not None and ri == 0:
60
+ lines.append((" "*gap).join("-"*w for w in widths).rstrip())
61
+ return "<pre>" + _e("\n".join(lines)) + "</pre>"
62
+ @staticmethod
63
+ def btn(text, data=None, url=None):
64
+ if (data is None) == (url is None): raise ValueError("حدد data أو url فقط")
65
+ return {"text": str(text), "callback_data": str(data)} if data is not None else {"text": str(text), "url": str(url)}
66
+ @staticmethod
67
+ def buttons(*rows):
68
+ try:
69
+ from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
70
+ except ImportError as e:
71
+ raise RuntimeError("ثبت المكتبة: pip install pyTelegramBotAPI") from e
72
+ markup = InlineKeyboardMarkup()
73
+ for row in rows:
74
+ if not isinstance(row, (list, tuple)): row = [row]
75
+ built = []
76
+ for item in row:
77
+ if isinstance(item, dict):
78
+ spec = dict(item); label = spec.pop("text", spec.pop("label", "Button"))
79
+ built.append(InlineKeyboardButton(str(label), **spec))
80
+ elif isinstance(item, str):
81
+ built.append(InlineKeyboardButton(item, callback_data=item))
82
+ elif isinstance(item, (list, tuple)) and len(item) >= 2:
83
+ label, target = str(item[0]), str(item[1])
84
+ if target.startswith("url:"): built.append(InlineKeyboardButton(label, url=target[4:]))
85
+ elif target.startswith("webapp:"): built.append(InlineKeyboardButton(label, web_app=WebAppInfo(target[7:])))
86
+ else: built.append(InlineKeyboardButton(label, callback_data=target[9:] if target.startswith("callback:") else target))
87
+ else: raise ValueError("صيغة زر غير صحيحة")
88
+ if built: markup.row(*built)
89
+ return markup
90
+ # Builders for Telegram Rich Message JSON. These build data; they do not send it.
91
+ @staticmethod
92
+ def rb(text): return {"type":"paragraph", "text":str(text)}
93
+ @staticmethod
94
+ def rh(text, size=1): return {"type":"heading", "text":str(text), "size":max(1,min(6,int(size)))}
95
+ @staticmethod
96
+ def rdiv(): return {"type":"divider"}
97
+ @staticmethod
98
+ def rtable(rows, header=True, bordered=True, striped=False, compact=False):
99
+ cells = [[{"text":str(v), **({"is_header":True} if header and ri==0 else {})} for v in row] for ri,row in enumerate(rows)]
100
+ return {"type":"table","cells":cells,"is_bordered":bool(bordered),"is_striped":bool(striped),"is_compact":bool(compact)}
101
+ @staticmethod
102
+ def rbutton(text, **action):
103
+ if len(action)!=1: raise ValueError("زر Rich يحتاج إجراء واحد")
104
+ return {"text":str(text), **action}
105
+ @staticmethod
106
+ def rbuttons(*rows, align="center"):
107
+ return {"type":"buttons","buttons":[dict(x) for row in rows for x in row],"align":align if align in ("left","center","right") else "center"}
108
+ @staticmethod
109
+ def rich(*blocks, is_rtl=True): return {"is_rtl":bool(is_rtl),"blocks":list(blocks)}
110
+ @staticmethod
111
+ def rich_json(*blocks, is_rtl=True, indent=2):
112
+ return json.dumps({"rich_message":K.rich(*blocks,is_rtl=is_rtl)},ensure_ascii=False,indent=indent)
113
+ @staticmethod
114
+ def help():
115
+ return """KFormat guide
116
+ b(text) عريض
117
+ i(text) مائل
118
+ un(text) تسطير
119
+ st(text) مشطوب
120
+ sp(text) نص مخفي
121
+ c(text) كود داخل السطر
122
+ pre(text,lang) كتلة كود
123
+ a(text,url) رابط
124
+ tg(text,id) منشن بالآيدي
125
+ block(text) اقتباس
126
+ expand(text) اقتباس قابل للطي
127
+ table(rows, headers=[]) جدول نصي
128
+ buttons(*rows) أزرار Inline
129
+ btn(text,data=...) أو btn(text,url=...)
130
+ ul(*items), ol(*items), bullet(text), hr()
131
+ rb/rh/rdiv/rtable/rbutton/rbuttons/rich/rich_json لبناء Rich Message JSON
132
+
133
+ دوال النص تحتاج parse_mode='HTML'.
134
+ الجدول العادي نص monospace مرتب، مو خلايا أصلية في sendMessage.
135
+ دوال Rich تبني JSON فقط، ولا ترسله وحدها.
136
+ """
137
+ bold=K.b
138
+ italic=K.i
139
+ underline=K.un
140
+ strike=K.st
141
+ spoiler=K.sp
142
+ code=K.c
143
+ table=K.table
144
+ buttons=K.buttons
File without changes