Просмотр исходного кода

TG 发送消息允许超过1024

doge 1 день назад
Родитель
Сommit
6ca0d3d767

+ 30 - 3
app/Services/TelegramChannelService.php

@@ -225,6 +225,9 @@ class TelegramChannelService
         }
 
         $messageId = $response['message_id'] ?? null;
+        if (isset($response['body_error'])) {
+            return ['sent' => null, 'pinned' => null, 'message_id' => $messageId, 'error' => $response['body_error']];
+        }
         if ($messageId === null) {
             return ['sent' => null, 'pinned' => null, 'message_id' => null, 'error' => '未能确认发布结果,请先检查频道'];
         }
@@ -252,10 +255,31 @@ class TelegramChannelService
             return $telegram->post('sendMessage', $params + ['text' => $channel['text']])->getResult() ?: [];
         }
 
+        $text = $channel['text'];
+        $separateBody = mb_strlen($text) > 1024;
+        $response = $this->sendMedia($telegram, $channel, $params + ['caption' => $separateBody ? '' : $text]);
+        if ($separateBody && isset($response['message_id'])) {
+            try {
+                $body = $telegram->post('sendMessage', [
+                    'chat_id' => $params['chat_id'],
+                    'text' => $text,
+                ])->getResult();
+                if (isset($body['message_id'])) {
+                    return $response;
+                }
+            } catch (\Exception) {
+                // The media was already sent. Do not retry the entire message.
+            }
+            $response['body_error'] = '媒体已发送,正文发送结果未确认,请在 Telegram 中核对';
+        }
+        return $response;
+    }
+
+    private function sendMedia(Api $telegram, array $channel, array $params): array
+    {
         $image = $channel['type'] === 'image';
         $media = $channel[$channel['type']];
         $field = $image ? 'photo' : 'video';
-        $params['caption'] = $channel['text'];
 
         // Only read files produced by the admin upload endpoints on the configured public disk.
         $disk = Storage::disk('public');
@@ -489,10 +513,13 @@ class TelegramChannelService
         $keyboard = $menu['buttons'] === []
             ? ['remove_keyboard' => true]
             : ['keyboard' => $menu['buttons'], 'resize_keyboard' => true];
-        $this->send($telegram, ['text' => $menu['reply']] + $menu + ['type' => 'text'], [
+        $response = $this->send($telegram, ['text' => $menu['reply']] + $menu + ['type' => 'text'], [
             'chat_id' => $chatId,
             'reply_markup' => json_encode($keyboard, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE),
         ]);
+        if (isset($response['body_error'])) {
+            Log::warning('Telegram 私聊菜单正文发送结果未确认', ['message_id' => $response['message_id']]);
+        }
     }
 
     private function findBot(int $id): object
@@ -593,7 +620,7 @@ class TelegramChannelService
     {
         $data = Validator::make($input, [
             'type' => 'required|in:text,image,video',
-            'text' => 'nullable|required_if:type,text|string|max:' . (($input['type'] ?? null) === 'text' ? 4096 : 1024),
+            'text' => 'nullable|required_if:type,text|string|max:4096',
             'image' => 'nullable|required_if:type,image|string|max:2048|url:http,https',
             'video' => 'nullable|required_if:type,video|string|max:2048|url:http,https',
             'button' => 'sometimes|array|list',

+ 2 - 0
docs/前端交付/bot-28/后台/Telegram频道管理-前端接入.md

@@ -57,3 +57,5 @@
 开始回复可选 `type: text | image | video`。文本填写 `reply`。图片或视频复用频道消息上传接口,将返回地址填入 `image` 或 `video`,`reply` 可作为说明。旧请求省略 `type` 时按文本处理。保存后向该机器人发送 `/start`,回复和底部键盘会一起下发。
 
 多语言表单按按钮所在行和列对应中文、英文、越南语。批量保存时提交 `menus: { "zh": { ... }, "en": { ... }, "vi": { ... } }`,每个语言的 `buttons` 为二维字符串数组。未配置的语言可省略,旧客户端也可继续传 `language` 保存单一语言。
+
+频道消息和私聊开始回复的正文最多 4096 字符。图片或视频的正文超过 1024 字符时,先发媒体,再单独发送完整正文。按钮仍附在媒体消息上。

+ 27 - 0
tests/Integration/TelegramChannelTest.php

@@ -526,6 +526,33 @@ class TelegramChannelTest extends TestCase
         parse_str((string) $this->requests[0]['request']->getBody(), $sent);
         $this->assertSame($media, $sent['photo']);
     }
+    public function test_long_media_body_is_sent_in_full_and_partial_failure_is_reported(): void
+    {
+        $this->migrate();
+        $this->app->register(FilesystemServiceProvider::class);
+        $text = str_repeat('正文', 600);
+        $channel = $this->service->save([
+            'chatId' => '@first_channel', 'type' => 'image',
+            'image' => 'https://files.example.test/photo.jpg', 'text' => $text,
+        ]);
+        $this->telegramResult(['message_id' => 18]);
+        $this->telegramResult(['message_id' => 19]);
+
+        $this->assertTrue($this->service->publish($channel['id'], false)['sent']);
+        parse_str((string) $this->requests[0]['request']->getBody(), $media);
+        parse_str((string) $this->requests[1]['request']->getBody(), $body);
+        $this->assertSame('', $media['caption']);
+        $this->assertSame($text, $body['text']);
+
+        $this->telegramResult(['message_id' => 20]);
+        $this->telegram->append(new Response(400, [], '{"ok":false,"error_code":400,"description":"rejected"}'));
+        $result = $this->service->publish($channel['id'], true);
+        $this->assertNull($result['sent']);
+        $this->assertSame(20, $result['message_id']);
+        $this->assertStringContainsString('媒体已发送', $result['error']);
+        $this->assertCount(4, $this->requests);
+    }
+
     public function test_admin_upload_is_sent_as_a_file_and_migration_can_be_retried(): void
     {
         $this->migrate();