以下是完整的 Telegram 类实现,并附带了多个常见使用场景的示例代码:
<?php
namespace App\Utility;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
final class Telegram {
private $bot = "";
private $channel = "";
private $client;
private $apiBaseUrl;
public function __construct() {
$this->client = new Client([
'timeout' => 10,
'http_errors' => false
]);
$this->apiBaseUrl = "https://api.telegram.org/bot" . $this->bot;
}
/**
* 发送富文本消息(可包含文字、链接和格式)
*
* @param string $text 要发送的HTML格式文本
* @param array|null $inlineKeyboard 可选的内联键盘配置
* @return array|false 成功返回响应数组,失败返回false
*/
public function sendText(string $text, array $inlineKeyboard = null) {
$postData = [
"chat_id" => $this->channel,
"text" => $text,
"parse_mode" => "HTML", // 支持HTML格式
"disable_web_page_preview" => false // 允许显示链接预览
];
// 添加内联键盘
if ($inlineKeyboard) {
$postData['reply_markup'] = [
'inline_keyboard' => $inlineKeyboard
];
}
return $this->jsonPost("/sendMessage", $postData);
}
/**
* 发送图片,可附带文字和链接
*
* @param string $imageUrl 图片URL或本地文件路径
* @param string $caption 图片说明文字(支持HTML格式、链接)
* @param array|null $inlineKeyboard 可选的内联键盘配置
* @return array|false 成功返回响应数组,失败返回false
*/
public function sendPhoto(string $imageUrl, string $caption = '', array $inlineKeyboard = null) {
$postData = [
"chat_id" => $this->channel,
"caption" => $caption,
"parse_mode" => "HTML" // 支持HTML格式
];
// 添加内联键盘
if ($inlineKeyboard) {
$postData['reply_markup'] = [
'inline_keyboard' => $inlineKeyboard
];
}
// 判断是URL还是本地文件
if (filter_var($imageUrl, FILTER_VALIDATE_URL)) {
$postData["photo"] = $imageUrl;
return $this->jsonPost("/sendPhoto", $postData);
} else {
// 本地文件需要使用multipart/form-data
$multipart = [
[
'name' => 'chat_id',
'contents' => $this->channel
],
[
'name' => 'caption',
'contents' => $caption
],
[
'name' => 'parse_mode',
'contents' => 'HTML'
],
[
'name' => 'photo',
'contents' => fopen($imageUrl, 'r'),
'filename' => basename($imageUrl)
]
];
// 添加内联键盘到multipart请求
if ($inlineKeyboard) {
$multipart[] = [
'name' => 'reply_markup',
'contents' => json_encode(['inline_keyboard' => $inlineKeyboard])
];
}
return $this->multipartPost("/sendPhoto", $multipart);
}
}
/**
* 发送媒体组(多图片、视频等)
*
* @param array $mediaItems 媒体项目数组
* @param string $caption 可选的共享说明文字
* @return array|false 成功返回响应数组,失败返回false
*/
public function sendMediaGroup(array $mediaItems, string $caption = '') {
$media = [];
$files = [];
$fileIndex = 0;
foreach ($mediaItems as $index => $item) {
$mediaItem = [
'type' => $item['type'] ?? 'photo', // 默认为图片
'media' => '',
];
if (!empty($caption) && $index === 0) {
$mediaItem['caption'] = $caption;
$mediaItem['parse_mode'] = 'HTML';
}
// 处理媒体文件
if (isset($item['media'])) {
// URL类型
if (filter_var($item['media'], FILTER_VALIDATE_URL)) {
$mediaItem['media'] = $item['media'];
}
// 本地文件
else if (file_exists($item['media'])) {
$attachName = "file{$fileIndex}";
$mediaItem['media'] = "attach://{$attachName}";
$files[] = [
'name' => $attachName,
'contents' => fopen($item['media'], 'r'),
'filename' => basename($item['media'])
];
$fileIndex++;
}
}
$media[] = $mediaItem;
}
$multipart = [
[
'name' => 'chat_id',
'contents' => $this->channel
],
[
'name' => 'media',
'contents' => json_encode($media)
]
];
// 添加本地文件
if (!empty($files)) {
$multipart = array_merge($multipart, $files);
}
return $this->multipartPost("/sendMediaGroup", $multipart);
}
/**
* 多功能推送方法 - 支持同时发送文字、图片和链接
*
* @param array|string $content 要发送的内容
* @return array|false 成功返回响应数组,失败返回false
*/
public function push($content) {
// 如果是字符串,转换为数组格式以统一处理
if (is_string($content)) {
$content = ['text' => $content];
}
// 提取内联键盘配置(如果有)
$inlineKeyboard = null;
if (isset($content['inline_keyboard'])) {
$inlineKeyboard = $content['inline_keyboard'];
} elseif (isset($content['reply_markup']) && isset($content['reply_markup']['inline_keyboard'])) {
$inlineKeyboard = $content['reply_markup']['inline_keyboard'];
}
// 场景1: 有多张图片/视频 - 使用sendMediaGroup
if (isset($content['media']) && is_array($content['media']) && count($content['media']) > 1) {
$caption = $content['text'] ?? '';
return $this->sendMediaGroup($content['media'], $caption);
}
// 场景2: 单张图片+文字 - 使用sendPhoto
if (isset($content['image']) || (isset($content['media']) && is_array($content['media']) && count($content['media']) == 1)) {
$imageUrl = $content['image'] ?? $content['media'][0]['media'];
$caption = $content['text'] ?? '';
return $this->sendPhoto($imageUrl, $caption, $inlineKeyboard);
}
// 场景3: 纯文本+链接 - 使用sendText
$message = '';
// 添加文本内容
if (isset($content['text'])) {
$message .= $content['text'];
}
// 添加链接
if (isset($content['links']) && is_array($content['links'])) {
if (!empty($message)) {
$message .= "\n\n";
}
foreach ($content['links'] as $link) {
$title = $link['title'] ?? $link['url'];
$message .= "<a href=\"{$link['url']}\">{$title}</a>\n";
}
} else if (isset($content['link'])) {
if (!empty($message)) {
$message .= "\n\n";
}
$title = $content['link_title'] ?? $content['link'];
$message .= "<a href=\"{$content['link']}\">{$title}</a>";
}
// 发送最终消息
return $this->sendText($message, $inlineKeyboard);
}
/**
* 创建内联键盘的便捷方法
*
* @param array $buttons 按钮配置数组
* @param int $columns 每行按钮数量,默认为1
* @return array 格式化的内联键盘数组
*/
public function createInlineKeyboard(array $buttons, int $columns = 1) {
$keyboard = [];
$row = [];
foreach ($buttons as $index => $button) {
// 创建按钮
$buttonData = [];
// URL类型按钮
if (isset($button['url'])) {
$buttonData = [
'text' => $button['text'],
'url' => $button['url']
];
}
// 回调类型按钮
elseif (isset($button['callback_data'])) {
$buttonData = [
'text' => $button['text'],
'callback_data' => $button['callback_data']
];
}
$row[] = $buttonData;
// 当达到指定列数或最后一个按钮时,添加行
if (count($row) >= $columns || $index == count($buttons) - 1) {
$keyboard[] = $row;
$row = [];
}
}
return $keyboard;
}
/**
* 发送JSON POST请求
*
* @param string $endpoint API端点
* @param array $postData 请求数据
* @return array|false 成功返回响应数组,失败返回false
*/
private function jsonPost(string $endpoint, array $postData) {
try {
$response = $this->client->post($this->apiBaseUrl . $endpoint, [
'json' => $postData
]);
$responseBody = json_decode($response->getBody(), true);
if (isset($responseBody['ok']) && $responseBody['ok'] === true) {
return $responseBody;
}
return false;
} catch (GuzzleException $e) {
// 记录错误或处理异常
return false;
}
}
/**
* 发送multipart/form-data POST请求
*
* @param string $endpoint API端点
* @param array $multipart 多部分表单数据
* @return array|false 成功返回响应数组,失败返回false
*/
private function multipartPost(string $endpoint, array $multipart) {
try {
$response = $this->client->post($this->apiBaseUrl . $endpoint, [
'multipart' => $multipart
]);
$responseBody = json_decode($response->getBody(), true);
if (isset($responseBody['ok']) && $responseBody['ok'] === true) {
return $responseBody;
}
return false;
} catch (GuzzleException $e) {
// 记录错误或处理异常
return false;
}
}
}
使用示例
1. 发送普通文本消息
<?php
require_once 'vendor/autoload.php';
use App\Utility\Telegram;
$telegram = new Telegram();
// 发送简单的纯文本消息
$telegram->push('这是一条简单的文本消息');
// 发送带格式的HTML文本消息
$telegram->push([
'text' => '<b>重要通知</b>: 系统将于今晚<i>10点</i>进行维护。'
]);
2. 发送带链接的消息
<?php
require_once 'vendor/autoload.php';
use App\Utility\Telegram;
$telegram = new Telegram();
// 方法1: 使用link和link_title字段
$telegram->push([
'text' => '请查看最新的系统公告:',
'link' => 'https://example.com/announcements',
'link_title' => '点击查看详情'
]);
// 方法2: 使用HTML标签直接在文本中嵌入链接
$telegram->push([
'text' => '请查看最新的<a href="https://example.com/announcements">系统公告</a>'
]);
// 方法3: 发送多个链接
$telegram->push([
'text' => '以下是重要链接:',
'links' => [
['url' => 'https://example.com/help', 'title' => '帮助中心'],
['url' => 'https://example.com/faq', 'title' => '常见问题']
]
]);
3. 发送图片
<?php
require_once 'vendor/autoload.php';
use App\Utility\Telegram;
$telegram = new Telegram();
// 发送网络图片
$telegram->push([
'image' => 'https://example.com/images/photo.jpg',
'text' => '这是一张网络图片'
]);
// 发送本地图片
$telegram->push([
'image' => '/path/to/local/image.jpg',
'text' => '这是一张本地图片'
]);
// 发送带HTML格式说明的图片
$telegram->push([
'image' => 'https://example.com/images/chart.png',
'text' => '这是<b>数据图表</b>,显示了<a href="https://example.com/stats">最新统计数据</a>'
]);
4. 发送多张照片
<?php
require_once 'vendor/autoload.php';
use App\Utility\Telegram;
$telegram = new Telegram();
// 发送多张图片
$telegram->push([
'text' => '这是一组产品图片',
'media' => [
['type' => 'photo', 'media' => 'https://example.com/images/product1.jpg'],
['type' => 'photo', 'media' => 'https://example.com/images/product2.jpg'],
['type' => 'photo', 'media' => 'https://example.com/images/product3.jpg']
]
]);
// 混合网络图片和本地图片
$telegram->push([
'text' => '这是一组混合图片',
'media' => [
['type' => 'photo', 'media' => 'https://example.com/images/online1.jpg'],
['type' => 'photo', 'media' => '/path/to/local/image1.jpg'],
['type' => 'photo', 'media' => '/path/to/local/image2.jpg']
]
]);
5. 发送带内联键盘按钮的消息
<?php
require_once 'vendor/autoload.php';
use App\Utility\Telegram;
$telegram = new Telegram();
// 方法1: 使用您之前提到的格式
$phone = "123456789";
$config = ['url' => 'https://example.com'];
$markup = "点击更新";
$reply_markup = json_decode('{"type": "InlineKeyboardMarkup","inline_keyboard": [[{"text": "'.$markup.'","url": "'.$config['url'].'/updata/'.$phone.'"}]]}', true);
$telegram->push([
'text' => '请点击按钮更新您的信息',
'reply_markup' => $reply_markup
]);
// 方法2: 使用inline_keyboard字段直接定义
$telegram->push([
'text' => '请选择一个操作:',
'inline_keyboard' => [
[
['text' => '查看详情', 'url' => 'https://example.com/details'],
['text' => '立即购买', 'url' => 'https://example.com/buy']
],
[
['text' => '取消', 'callback_data' => 'cancel_action']
]
]
]);
// 方法3: 使用createInlineKeyboard助手方法
$buttons = [
['text' => '按钮1', 'url' => 'https://example.com/btn1'],
['text' => '按钮2', 'url' => 'https://example.com/btn2'],
['text' => '按钮3', 'url' => 'https://example.com/btn3']
];
// 创建2列的键盘布局
$keyboard = $telegram->createInlineKeyboard($buttons, 2);
$telegram->push([
'text' => '使用助手方法创建的按钮:',
'inline_keyboard' => $keyboard
]);
6. 发送图片 + 文字 + 按钮的组合消息
<?php
require_once 'vendor/autoload.php';
use App\Utility\Telegram;
$telegram = new Telegram();
// 组合所有元素: 图片+文字+链接+按钮
$telegram->push([
'image' => 'https://example.com/images/product.jpg',
'text' => '<b>新产品上市!</b>\n\n这是我们最新推出的产品,具有以下特点:\n- 高性能\n- 低功耗\n- 智能操控\n\n详情请访问<a href="https://example.com/product">产品页面</a>',
'inline_keyboard' => [
[
['text' => '立即购买', 'url' => 'https://example.com/buy'],
['text' => '加入购物车', 'callback_data' => 'add_to_cart_123']
],
[
['text' => '查看评价', 'url' => 'https://example.com/reviews']
]
]
]);
7. 智能内容检测示例
<?php
require_once 'vendor/autoload.php';
use App\Utility\Telegram;
$telegram = new Telegram();
// push方法会自动检测内容类型
function sendContent($content) {
global $telegram;
return $telegram->push($content);
}
// 自动检测为文本
sendContent('这是一条普通消息');
// 自动检测为图片
sendContent([
'image' => 'https://example.com/image.jpg'
]);
// 自动检测为带说明的图片
sendContent([
'image' => 'https://example.com/image.jpg',
'text' => '图片说明'
]);
// 自动检测为带按钮的消息
sendContent([
'text' => '带按钮的消息',
'inline_keyboard' => [
[
['text' => '点击这里', 'url' => 'https://example.com']
]
]
]);
// 自动检测为多媒体组
sendContent([
'media' => [
['type' => 'photo', 'media' => 'https://example.com/image1.jpg'],
['type' => 'photo', 'media' => 'https://example.com/image2.jpg']
],
'text' => '多媒体组说明'
]);
这些示例涵盖了大多数常见的使用场景,从简单的文本消息到复杂的组合内容。可以根据实际需求选择合适的方式发送消息。该类的设计允许灵活组合各种元素,同时保持了简洁的 API 接口。