or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-features.mdapplication-framework.mdbot-api.mdfiles.mdfilters.mdhandlers.mdindex.mdkeyboards.mdtelegram-types.md

keyboards.mddocs/

0

# Keyboards and Markup

1

2

Comprehensive keyboard system for creating interactive interfaces including reply keyboards, inline keyboards, and specialized button types for web apps, contact/location requests, and callback data.

3

4

## Capabilities

5

6

### Reply Keyboards

7

8

Custom keyboards that replace the standard keyboard in the user's Telegram client.

9

10

```python { .api }

11

class ReplyKeyboardMarkup:

12

def __init__(

13

self,

14

keyboard: list[list[KeyboardButton]],

15

is_persistent: bool = None,

16

resize_keyboard: bool = None,

17

one_time_keyboard: bool = None,

18

input_field_placeholder: str = None,

19

selective: bool = None

20

): ...

21

22

keyboard: list[list[KeyboardButton]]

23

is_persistent: bool | None

24

resize_keyboard: bool | None

25

one_time_keyboard: bool | None

26

input_field_placeholder: str | None

27

selective: bool | None

28

29

@classmethod

30

def from_button(cls, button: KeyboardButton, **kwargs) -> 'ReplyKeyboardMarkup': ...

31

@classmethod

32

def from_row(cls, *args: KeyboardButton, **kwargs) -> 'ReplyKeyboardMarkup': ...

33

@classmethod

34

def from_column(cls, *args: KeyboardButton, **kwargs) -> 'ReplyKeyboardMarkup': ...

35

36

class KeyboardButton:

37

def __init__(

38

self,

39

text: str,

40

request_users: KeyboardButtonRequestUsers = None,

41

request_chat: KeyboardButtonRequestChat = None,

42

request_contact: bool = None,

43

request_location: bool = None,

44

request_poll: KeyboardButtonPollType = None,

45

web_app: WebAppInfo = None

46

): ...

47

48

text: str

49

request_users: KeyboardButtonRequestUsers | None

50

request_chat: KeyboardButtonRequestChat | None

51

request_contact: bool | None

52

request_location: bool | None

53

request_poll: KeyboardButtonPollType | None

54

web_app: WebAppInfo | None

55

56

class ReplyKeyboardRemove:

57

def __init__(self, selective: bool = None): ...

58

59

remove_keyboard: bool = True

60

selective: bool | None

61

```

62

63

Usage examples:

64

65

```python

66

from telegram import ReplyKeyboardMarkup, KeyboardButton

67

68

# Simple text buttons

69

keyboard = [

70

["Option 1", "Option 2"],

71

["Option 3", "Option 4"]

72

]

73

reply_markup = ReplyKeyboardMarkup(keyboard, resize_keyboard=True)

74

75

# Using KeyboardButton objects

76

buttons = [

77

[KeyboardButton("Share Contact", request_contact=True)],

78

[KeyboardButton("Share Location", request_location=True)],

79

[KeyboardButton("Web App", web_app=WebAppInfo(url="https://example.com"))]

80

]

81

reply_markup = ReplyKeyboardMarkup(buttons, one_time_keyboard=True)

82

83

# Send message with keyboard

84

await bot.send_message(

85

chat_id=chat_id,

86

text="Choose an option:",

87

reply_markup=reply_markup

88

)

89

90

# Remove keyboard

91

from telegram import ReplyKeyboardRemove

92

await bot.send_message(

93

chat_id=chat_id,

94

text="Keyboard removed",

95

reply_markup=ReplyKeyboardRemove()

96

)

97

```

98

99

### Inline Keyboards

100

101

Keyboards with buttons that appear directly below messages, supporting callbacks, URLs, and other actions.

102

103

```python { .api }

104

class InlineKeyboardMarkup:

105

def __init__(self, inline_keyboard: list[list[InlineKeyboardButton]]): ...

106

107

inline_keyboard: list[list[InlineKeyboardButton]]

108

109

@classmethod

110

def from_button(cls, button: InlineKeyboardButton) -> 'InlineKeyboardMarkup': ...

111

@classmethod

112

def from_row(cls, *args: InlineKeyboardButton) -> 'InlineKeyboardMarkup': ...

113

@classmethod

114

def from_column(cls, *args: InlineKeyboardButton) -> 'InlineKeyboardMarkup': ...

115

116

class InlineKeyboardButton:

117

def __init__(

118

self,

119

text: str,

120

url: str = None,

121

callback_data: str = None,

122

web_app: WebAppInfo = None,

123

login_url: LoginUrl = None,

124

switch_inline_query: str = None,

125

switch_inline_query_current_chat: str = None,

126

switch_inline_query_chosen_chat: SwitchInlineQueryChosenChat = None,

127

copy_text: CopyTextButton = None,

128

callback_game: CallbackGame = None,

129

pay: bool = None

130

): ...

131

132

text: str

133

url: str | None

134

callback_data: str | None

135

web_app: WebAppInfo | None

136

login_url: LoginUrl | None

137

switch_inline_query: str | None

138

switch_inline_query_current_chat: str | None

139

switch_inline_query_chosen_chat: SwitchInlineQueryChosenChat | None

140

copy_text: CopyTextButton | None

141

callback_game: CallbackGame | None

142

pay: bool | None

143

```

144

145

Usage examples:

146

147

```python

148

from telegram import InlineKeyboardMarkup, InlineKeyboardButton

149

150

# Callback data buttons

151

keyboard = [

152

[

153

InlineKeyboardButton("Option A", callback_data="opt_a"),

154

InlineKeyboardButton("Option B", callback_data="opt_b")

155

],

156

[InlineKeyboardButton("Help", callback_data="help")]

157

]

158

reply_markup = InlineKeyboardMarkup(keyboard)

159

160

# URL buttons

161

keyboard = [

162

[InlineKeyboardButton("Visit Website", url="https://example.com")],

163

[InlineKeyboardButton("Telegram Channel", url="https://t.me/channel")]

164

]

165

reply_markup = InlineKeyboardMarkup(keyboard)

166

167

# Mixed button types

168

keyboard = [

169

[InlineKeyboardButton("Callback", callback_data="cb_data")],

170

[InlineKeyboardButton("URL", url="https://example.com")],

171

[InlineKeyboardButton("Switch Inline", switch_inline_query="query")],

172

[InlineKeyboardButton("Web App", web_app=WebAppInfo(url="https://webapp.com"))]

173

]

174

reply_markup = InlineKeyboardMarkup(keyboard)

175

176

await bot.send_message(

177

chat_id=chat_id,

178

text="Choose an action:",

179

reply_markup=reply_markup

180

)

181

```

182

183

### Force Reply

184

185

Force users to reply to a specific message.

186

187

```python { .api }

188

class ForceReply:

189

def __init__(

190

self,

191

input_field_placeholder: str = None,

192

selective: bool = None

193

): ...

194

195

force_reply: bool = True

196

input_field_placeholder: str | None

197

selective: bool | None

198

```

199

200

Usage example:

201

202

```python

203

from telegram import ForceReply

204

205

await bot.send_message(

206

chat_id=chat_id,

207

text="Please tell me your name:",

208

reply_markup=ForceReply(input_field_placeholder="Enter your name...")

209

)

210

```

211

212

### Specialized Button Types

213

214

Advanced button configurations for specific use cases.

215

216

```python { .api }

217

class KeyboardButtonRequestUsers:

218

def __init__(

219

self,

220

request_id: int,

221

user_is_bot: bool = None,

222

user_is_premium: bool = None,

223

max_quantity: int = None,

224

request_name: bool = None,

225

request_username: bool = None,

226

request_photo: bool = None

227

): ...

228

229

request_id: int

230

user_is_bot: bool | None

231

user_is_premium: bool | None

232

max_quantity: int | None

233

request_name: bool | None

234

request_username: bool | None

235

request_photo: bool | None

236

237

class KeyboardButtonRequestChat:

238

def __init__(

239

self,

240

request_id: int,

241

chat_is_channel: bool,

242

chat_is_forum: bool = None,

243

chat_has_username: bool = None,

244

chat_is_created: bool = None,

245

user_administrator_rights: ChatAdministratorRights = None,

246

bot_administrator_rights: ChatAdministratorRights = None,

247

bot_is_member: bool = None,

248

request_title: bool = None,

249

request_username: bool = None,

250

request_photo: bool = None

251

): ...

252

253

request_id: int

254

chat_is_channel: bool

255

chat_is_forum: bool | None

256

chat_has_username: bool | None

257

chat_is_created: bool | None

258

user_administrator_rights: ChatAdministratorRights | None

259

bot_administrator_rights: ChatAdministratorRights | None

260

bot_is_member: bool | None

261

request_title: bool | None

262

request_username: bool | None

263

request_photo: bool | None

264

265

class KeyboardButtonPollType:

266

def __init__(self, type: str = None): ...

267

268

type: str | None

269

270

class WebAppInfo:

271

def __init__(self, url: str): ...

272

273

url: str

274

275

class LoginUrl:

276

def __init__(

277

self,

278

url: str,

279

forward_text: str = None,

280

bot_username: str = None,

281

request_write_access: bool = None

282

): ...

283

284

url: str

285

forward_text: str | None

286

bot_username: str | None

287

request_write_access: bool | None

288

289

class SwitchInlineQueryChosenChat:

290

def __init__(

291

self,

292

query: str = None,

293

allow_user_chats: bool = None,

294

allow_bot_chats: bool = None,

295

allow_group_chats: bool = None,

296

allow_channel_chats: bool = None

297

): ...

298

299

query: str | None

300

allow_user_chats: bool | None

301

allow_bot_chats: bool | None

302

allow_group_chats: bool | None

303

allow_channel_chats: bool | None

304

305

class CopyTextButton:

306

def __init__(self, text: str): ...

307

308

text: str

309

```

310

311

Usage examples:

312

313

```python

314

# Request user selection

315

user_request = KeyboardButtonRequestUsers(

316

request_id=1,

317

user_is_bot=False,

318

max_quantity=5,

319

request_name=True,

320

request_username=True

321

)

322

button = KeyboardButton("Select Users", request_users=user_request)

323

324

# Request chat selection

325

chat_request = KeyboardButtonRequestChat(

326

request_id=2,

327

chat_is_channel=False,

328

chat_has_username=True,

329

request_title=True

330

)

331

button = KeyboardButton("Select Chat", request_chat=chat_request)

332

333

# Poll type button

334

poll_button = KeyboardButton(

335

"Create Poll",

336

request_poll=KeyboardButtonPollType(type="quiz")

337

)

338

339

# Web app button

340

webapp_button = InlineKeyboardButton(

341

"Open Web App",

342

web_app=WebAppInfo(url="https://your-webapp.com")

343

)

344

345

# Login URL button

346

login_button = InlineKeyboardButton(

347

"Login",

348

login_url=LoginUrl(

349

url="https://your-site.com/auth",

350

request_write_access=True

351

)

352

)

353

354

# Switch inline query button

355

switch_button = InlineKeyboardButton(

356

"Share",

357

switch_inline_query_chosen_chat=SwitchInlineQueryChosenChat(

358

query="shared content",

359

allow_user_chats=True,

360

allow_group_chats=True

361

)

362

)

363

```

364

365

### Dynamic Keyboard Building

366

367

Utility methods for building keyboards programmatically.

368

369

```python

370

# Build keyboard from lists

371

def build_menu(buttons, n_cols, header_buttons=None, footer_buttons=None):

372

"""Build a menu keyboard with specified column layout."""

373

menu = [buttons[i:i + n_cols] for i in range(0, len(buttons), n_cols)]

374

if header_buttons:

375

menu.insert(0, header_buttons)

376

if footer_buttons:

377

menu.append(footer_buttons)

378

return menu

379

380

# Example usage

381

buttons = [

382

InlineKeyboardButton(f"Option {i}", callback_data=f"opt_{i}")

383

for i in range(1, 10)

384

]

385

386

keyboard = build_menu(

387

buttons,

388

n_cols=3,

389

header_buttons=[InlineKeyboardButton("Header", callback_data="header")],

390

footer_buttons=[InlineKeyboardButton("Cancel", callback_data="cancel")]

391

)

392

393

reply_markup = InlineKeyboardMarkup(keyboard)

394

```

395

396

### Handling Keyboard Responses

397

398

Processing user interactions with keyboards.

399

400

```python

401

from telegram.ext import CallbackQueryHandler, MessageHandler, filters

402

403

# Handle callback query responses

404

async def button_handler(update, context):

405

query = update.callback_query

406

await query.answer() # Always acknowledge callback queries

407

408

data = query.data

409

if data == "opt_a":

410

await query.edit_message_text("You chose Option A")

411

elif data == "opt_b":

412

await query.edit_message_text("You chose Option B")

413

414

app.add_handler(CallbackQueryHandler(button_handler))

415

416

# Handle shared users/chats

417

async def handle_users_shared(update, context):

418

users_shared = update.message.users_shared

419

request_id = users_shared.request_id

420

users = users_shared.users

421

422

user_names = [f"{user.first_name}" for user in users if user.first_name]

423

await update.message.reply_text(f"You shared users: {', '.join(user_names)}")

424

425

async def handle_chat_shared(update, context):

426

chat_shared = update.message.chat_shared

427

request_id = chat_shared.request_id

428

chat_id = chat_shared.chat_id

429

430

await update.message.reply_text(f"You shared chat ID: {chat_id}")

431

432

# Filter for shared content

433

app.add_handler(MessageHandler(filters.StatusUpdate.USERS_SHARED, handle_users_shared))

434

app.add_handler(MessageHandler(filters.StatusUpdate.CHAT_SHARED, handle_chat_shared))

435

```

436

437

## Common Patterns

438

439

### Pagination Keyboards

440

441

```python

442

def create_pagination_keyboard(items, page, per_page=5):

443

"""Create paginated inline keyboard."""

444

start = page * per_page

445

end = start + per_page

446

current_items = items[start:end]

447

448

keyboard = []

449

450

# Item buttons

451

for item in current_items:

452

keyboard.append([InlineKeyboardButton(item['name'], callback_data=f"item_{item['id']}")])

453

454

# Navigation buttons

455

nav_buttons = []

456

if page > 0:

457

nav_buttons.append(InlineKeyboardButton("◀️ Previous", callback_data=f"page_{page-1}"))

458

if end < len(items):

459

nav_buttons.append(InlineKeyboardButton("Next ▶️", callback_data=f"page_{page+1}"))

460

461

if nav_buttons:

462

keyboard.append(nav_buttons)

463

464

return InlineKeyboardMarkup(keyboard)

465

```

466

467

### Confirmation Dialogs

468

469

```python

470

def create_confirmation_keyboard(action_data):

471

"""Create Yes/No confirmation keyboard."""

472

keyboard = [

473

[

474

InlineKeyboardButton("✅ Yes", callback_data=f"confirm_{action_data}"),

475

InlineKeyboardButton("❌ No", callback_data="cancel")

476

]

477

]

478

return InlineKeyboardMarkup(keyboard)

479

```

480

481

### Settings Menus

482

483

```python

484

def create_settings_keyboard(user_settings):

485

"""Create settings toggle keyboard."""

486

keyboard = []

487

488

for setting, value in user_settings.items():

489

status = "✅" if value else "❌"

490

keyboard.append([

491

InlineKeyboardButton(

492

f"{status} {setting.title()}",

493

callback_data=f"toggle_{setting}"

494

)

495

])

496

497

keyboard.append([InlineKeyboardButton("💾 Save", callback_data="save_settings")])

498

return InlineKeyboardMarkup(keyboard)

499

```