RESAZIP-PC\resaz

add daitogooglechat

...@@ -113,6 +113,88 @@ DaitoString::convertToUtf8('/tmp/source.csv'); ...@@ -113,6 +113,88 @@ DaitoString::convertToUtf8('/tmp/source.csv');
113 DaitoString::convertToUtf8('/tmp/source.csv', '/tmp/out', 'source_utf8.csv', 1); 113 DaitoString::convertToUtf8('/tmp/source.csv', '/tmp/out', 'source_utf8.csv', 1);
114 ``` 114 ```
115 115
116 +## DaitoGoogleChat (Laravel webhook utility)
117 +
118 +### 1) Publish config
119 +```bash
120 +php artisan vendor:publish --tag=daito-google-chat-config
121 +```
122 +
123 +This creates `config/daito-google-chat.php`.
124 +
125 +### 2) Minimal `.env` setup
126 +```dotenv
127 +DAITO_GOOGLE_CHAT_ENABLED=true
128 +DAITO_GOOGLE_CHAT_WEBHOOK_URL=https://chat.googleapis.com/v1/spaces/.../messages?key=...&token=...
129 +```
130 +
131 +### 3) Send immediately
132 +```php
133 +use Daito\Lib\DaitoGoogleChat;
134 +
135 +DaitoGoogleChat::sendText('Deploy success', array(
136 + 'service' => 'billing-api',
137 + 'env' => 'production',
138 +));
139 +```
140 +
141 +### 4) Send via queue
142 +```php
143 +use Daito\Lib\DaitoGoogleChat;
144 +
145 +DaitoGoogleChat::queueText('Large import finished', array(
146 + 'job_id' => 12345,
147 + 'duration' => '02:31',
148 +));
149 +```
150 +
151 +### 5) Advanced payload
152 +```php
153 +DaitoGoogleChat::sendPayload(array(
154 + 'text' => 'Custom payload from daito/lib',
155 +));
156 +```
157 +
158 +### 6) cardV2 (send immediately)
159 +```php
160 +use Daito\Lib\DaitoGoogleChat;
161 +
162 +$arrCardV2 = array(
163 + 'cardId' => 'deploy-status',
164 + 'card' => array(
165 + 'header' => array(
166 + 'title' => 'Deploy Success',
167 + 'subtitle' => 'billing-api / production',
168 + ),
169 + 'sections' => array(
170 + array(
171 + 'widgets' => array(
172 + array(
173 + 'decoratedText' => array(
174 + 'topLabel' => 'Version',
175 + 'text' => 'v2.3.1',
176 + ),
177 + ),
178 + array(
179 + 'decoratedText' => array(
180 + 'topLabel' => 'Duration',
181 + 'text' => '01:45',
182 + ),
183 + ),
184 + ),
185 + ),
186 + ),
187 + ),
188 +);
189 +
190 +DaitoGoogleChat::sendCardV2($arrCardV2);
191 +```
192 +
193 +### 7) cardV2 (send via queue)
194 +```php
195 +DaitoGoogleChat::queueCardV2($arrCardV2);
196 +```
197 +
116 ## QueryLog (Laravel shared package) 198 ## QueryLog (Laravel shared package)
117 199
118 > **Provider registration note** 200 > **Provider registration note**
......
...@@ -20,7 +20,8 @@ ...@@ -20,7 +20,8 @@
20 "extra": { 20 "extra": {
21 "laravel": { 21 "laravel": {
22 "providers": [ 22 "providers": [
23 - "Daito\\Lib\\DaitoQueryLog\\Providers\\DaitoQueryLogProvider" 23 + "Daito\\Lib\\DaitoQueryLog\\Providers\\DaitoQueryLogProvider",
24 + "Daito\\Lib\\DaitoGoogleChat\\Providers\\DaitoGoogleChatProvider"
24 ] 25 ]
25 } 26 }
26 } 27 }
......
1 +<?php
2 +
3 +namespace Daito\Lib;
4 +
5 +use Daito\Lib\DaitoGoogleChat\DaitoGoogleChatManager;
6 +use RuntimeException;
7 +
8 +class DaitoGoogleChat
9 +{
10 + public static function sendText($text, array $arrContext = array(), $webhookUrl = null): array
11 + {
12 + return self::manager()->sendText($text, $arrContext, $webhookUrl);
13 + }
14 +
15 + public static function sendPayload(array $arrPayload, $webhookUrl = null): array
16 + {
17 + return self::manager()->sendPayload($arrPayload, $webhookUrl);
18 + }
19 +
20 + public static function sendCardV2(array $arrCardV2, $webhookUrl = null): array
21 + {
22 + return self::manager()->sendCardV2($arrCardV2, $webhookUrl);
23 + }
24 +
25 + public static function sendCardsV2(array $arrCardsV2, $webhookUrl = null): array
26 + {
27 + return self::manager()->sendCardsV2($arrCardsV2, $webhookUrl);
28 + }
29 +
30 + public static function queueText($text, array $arrContext = array(), $webhookUrl = null): void
31 + {
32 + self::manager()->queueText($text, $arrContext, $webhookUrl);
33 + }
34 +
35 + public static function queuePayload(array $arrPayload, $webhookUrl = null): void
36 + {
37 + self::manager()->queuePayload($arrPayload, $webhookUrl);
38 + }
39 +
40 + public static function queueCardV2(array $arrCardV2, $webhookUrl = null): void
41 + {
42 + self::manager()->queueCardV2($arrCardV2, $webhookUrl);
43 + }
44 +
45 + public static function queueCardsV2(array $arrCardsV2, $webhookUrl = null): void
46 + {
47 + self::manager()->queueCardsV2($arrCardsV2, $webhookUrl);
48 + }
49 +
50 + private static function manager(): DaitoGoogleChatManager
51 + {
52 + if (!function_exists('app')) {
53 + throw new RuntimeException('Laravel app container is required for DaitoGoogleChat.');
54 + }
55 +
56 + $manager = app(DaitoGoogleChatManager::class);
57 + if (!$manager instanceof DaitoGoogleChatManager) {
58 + throw new RuntimeException('Can not resolve DaitoGoogleChatManager from container.');
59 + }
60 +
61 + return $manager;
62 + }
63 +}
1 +<?php
2 +
3 +namespace Daito\Lib\DaitoGoogleChat;
4 +
5 +use Daito\Lib\DaitoGoogleChat\Jobs\DaitoGoogleChatWebhookJob;
6 +use Daito\Lib\DaitoGoogleChat\Services\DaitoGoogleChatWebhookClient;
7 +
8 +class DaitoGoogleChatManager
9 +{
10 + /**
11 + * @var \Daito\Lib\DaitoGoogleChat\Services\DaitoGoogleChatWebhookClient
12 + */
13 + private $client;
14 +
15 + public function __construct(DaitoGoogleChatWebhookClient $client)
16 + {
17 + $this->client = $client;
18 + }
19 +
20 + public function sendText($text, array $arrContext = array(), $webhookUrl = null): array
21 + {
22 + return $this->sendPayload(
23 + array(
24 + 'text' => $this->buildTextContent((string) $text, $arrContext),
25 + ),
26 + $webhookUrl
27 + );
28 + }
29 +
30 + public function sendPayload(array $arrPayload, $webhookUrl = null): array
31 + {
32 + return $this->client->send($arrPayload, $webhookUrl);
33 + }
34 +
35 + public function sendCardV2(array $arrCardV2, $webhookUrl = null): array
36 + {
37 + return $this->sendCardsV2(array($arrCardV2), $webhookUrl);
38 + }
39 +
40 + public function sendCardsV2(array $arrCardsV2, $webhookUrl = null): array
41 + {
42 + return $this->sendPayload(
43 + array(
44 + 'cardsV2' => array_values($arrCardsV2),
45 + ),
46 + $webhookUrl
47 + );
48 + }
49 +
50 + public function queueText($text, array $arrContext = array(), $webhookUrl = null): void
51 + {
52 + $this->queuePayload(
53 + array(
54 + 'text' => $this->buildTextContent((string) $text, $arrContext),
55 + ),
56 + $webhookUrl
57 + );
58 + }
59 +
60 + public function queuePayload(array $arrPayload, $webhookUrl = null): void
61 + {
62 + $job = new DaitoGoogleChatWebhookJob($arrPayload, $webhookUrl);
63 + if (config('daito-google-chat.queue_connection') !== null) {
64 + $job->onConnection(config('daito-google-chat.queue_connection'));
65 + }
66 + if (config('daito-google-chat.queue_name') !== null) {
67 + $job->onQueue(config('daito-google-chat.queue_name'));
68 + }
69 +
70 + dispatch($job);
71 + }
72 +
73 + public function queueCardV2(array $arrCardV2, $webhookUrl = null): void
74 + {
75 + $this->queueCardsV2(array($arrCardV2), $webhookUrl);
76 + }
77 +
78 + public function queueCardsV2(array $arrCardsV2, $webhookUrl = null): void
79 + {
80 + $this->queuePayload(
81 + array(
82 + 'cardsV2' => array_values($arrCardsV2),
83 + ),
84 + $webhookUrl
85 + );
86 + }
87 +
88 + private function buildTextContent(string $text, array $arrContext = array()): string
89 + {
90 + $textContent = trim($text);
91 + if ($arrContext === array()) {
92 + return $textContent;
93 + }
94 +
95 + $arrContextLines = array();
96 + foreach ($arrContext as $key => $value) {
97 + $arrContextLines[] = sprintf('%s: %s', (string) $key, $this->normalizeContextValue($value));
98 + }
99 +
100 + return $textContent . "\n" . implode("\n", $arrContextLines);
101 + }
102 +
103 + private function normalizeContextValue($value): string
104 + {
105 + if (is_scalar($value) || $value === null) {
106 + return (string) $value;
107 + }
108 +
109 + $json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
110 + return $json !== false ? $json : '[unserializable]';
111 + }
112 +}
1 +<?php
2 +
3 +namespace Daito\Lib\DaitoGoogleChat\Jobs;
4 +
5 +use Daito\Lib\DaitoGoogleChat\Services\DaitoGoogleChatWebhookClient;
6 +use Illuminate\Bus\Queueable;
7 +use Illuminate\Contracts\Queue\ShouldQueue;
8 +use Illuminate\Foundation\Bus\Dispatchable;
9 +use Illuminate\Queue\InteractsWithQueue;
10 +use Illuminate\Queue\SerializesModels;
11 +
12 +class DaitoGoogleChatWebhookJob implements ShouldQueue
13 +{
14 + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
15 +
16 + protected $arrPayload;
17 + protected $webhookUrl;
18 +
19 + public $tries;
20 + public $backoff;
21 +
22 + public function __construct(array $arrPayload, $webhookUrl = null)
23 + {
24 + $this->arrPayload = $arrPayload;
25 + $this->webhookUrl = $webhookUrl !== null ? (string) $webhookUrl : null;
26 + $this->tries = max(1, (int) config('daito-google-chat.queue_tries', 3));
27 + $this->backoff = max(1, (int) config('daito-google-chat.queue_backoff_seconds', 10));
28 + }
29 +
30 + public function handle(DaitoGoogleChatWebhookClient $client): void
31 + {
32 + $client->send($this->arrPayload, $this->webhookUrl);
33 + }
34 +}
1 +<?php
2 +
3 +namespace Daito\Lib\DaitoGoogleChat\Providers;
4 +
5 +use Daito\Lib\DaitoGoogleChat\DaitoGoogleChatManager;
6 +use Daito\Lib\DaitoGoogleChat\Services\DaitoGoogleChatWebhookClient;
7 +use Illuminate\Support\ServiceProvider;
8 +
9 +class DaitoGoogleChatProvider extends ServiceProvider
10 +{
11 + public function register(): void
12 + {
13 + $this->mergeConfigFrom(
14 + __DIR__ . '/../config/daito-google-chat.php',
15 + 'daito-google-chat'
16 + );
17 +
18 + $this->app->singleton(DaitoGoogleChatWebhookClient::class, function () {
19 + return new DaitoGoogleChatWebhookClient();
20 + });
21 +
22 + $this->app->singleton(DaitoGoogleChatManager::class, function ($app) {
23 + return new DaitoGoogleChatManager(
24 + $app->make(DaitoGoogleChatWebhookClient::class)
25 + );
26 + });
27 + }
28 +
29 + public function boot(): void
30 + {
31 + $this->publishes(
32 + array(
33 + __DIR__ . '/../config/daito-google-chat.php' => config_path('daito-google-chat.php'),
34 + ),
35 + 'daito-google-chat-config'
36 + );
37 + }
38 +}
1 +<?php
2 +
3 +namespace Daito\Lib\DaitoGoogleChat\Services;
4 +
5 +use Illuminate\Support\Facades\Http;
6 +use Illuminate\Support\Facades\Log;
7 +use RuntimeException;
8 +
9 +class DaitoGoogleChatWebhookClient
10 +{
11 + public function send(array $arrPayload, $webhookUrl = null): array
12 + {
13 + if (!(bool) config('daito-google-chat.enabled', false)) {
14 + return array(
15 + 'success' => 0,
16 + 'status' => 'disabled',
17 + 'message' => 'Daito Google Chat notification is disabled.',
18 + );
19 + }
20 +
21 + $resolvedWebhookUrl = $this->resolveWebhookUrl($webhookUrl);
22 + $this->assertWebhookUrlIsAllowed($resolvedWebhookUrl);
23 +
24 + $response = Http::acceptJson()
25 + ->asJson()
26 + ->timeout((int) config('daito-google-chat.timeout_seconds', 5))
27 + ->connectTimeout((int) config('daito-google-chat.connect_timeout_seconds', 3))
28 + ->retry(
29 + (int) config('daito-google-chat.retry_times', 1),
30 + (int) config('daito-google-chat.retry_sleep_ms', 200)
31 + )
32 + ->post($resolvedWebhookUrl, $arrPayload);
33 +
34 + if ($response->successful()) {
35 + return array(
36 + 'success' => 1,
37 + 'status' => 'sent',
38 + 'http_code' => $response->status(),
39 + );
40 + }
41 +
42 + $arrError = array(
43 + 'success' => 0,
44 + 'status' => 'failed',
45 + 'http_code' => $response->status(),
46 + 'message' => trim((string) $response->body()),
47 + );
48 +
49 + Log::warning('DaitoGoogleChat send failed.', $arrError);
50 +
51 + if ((bool) config('daito-google-chat.throw_on_error', false)) {
52 + throw new RuntimeException('DaitoGoogleChat send failed: HTTP ' . (string) $response->status());
53 + }
54 +
55 + return $arrError;
56 + }
57 +
58 + private function resolveWebhookUrl($webhookUrl): string
59 + {
60 + $resolvedWebhookUrl = trim((string) ($webhookUrl ?: config('daito-google-chat.default_webhook_url', '')));
61 + if ($resolvedWebhookUrl === '') {
62 + throw new RuntimeException('Google Chat webhook URL is empty.');
63 + }
64 +
65 + return $resolvedWebhookUrl;
66 + }
67 +
68 + private function assertWebhookUrlIsAllowed(string $webhookUrl): void
69 + {
70 + if (!(bool) config('daito-google-chat.validate_webhook_host', true)) {
71 + return;
72 + }
73 +
74 + $host = strtolower((string) parse_url($webhookUrl, PHP_URL_HOST));
75 + if ($host === '') {
76 + throw new RuntimeException('Google Chat webhook URL is invalid.');
77 + }
78 +
79 + $arrAllowedWebhookHosts = array_map('strtolower', (array) config('daito-google-chat.allowed_webhook_hosts', array()));
80 + if (!in_array($host, $arrAllowedWebhookHosts, true)) {
81 + throw new RuntimeException('Google Chat webhook host is not allowed: ' . $host);
82 + }
83 + }
84 +}
1 +<?php
2 +
3 +return array(
4 + 'enabled' => env('DAITO_GOOGLE_CHAT_ENABLED', false),
5 + 'default_webhook_url' => env('DAITO_GOOGLE_CHAT_WEBHOOK_URL', ''),
6 + 'validate_webhook_host' => env('DAITO_GOOGLE_CHAT_VALIDATE_WEBHOOK_HOST', true),
7 + 'allowed_webhook_hosts' => array(
8 + 'chat.googleapis.com',
9 + ),
10 + 'timeout_seconds' => env('DAITO_GOOGLE_CHAT_TIMEOUT', 5),
11 + 'connect_timeout_seconds' => env('DAITO_GOOGLE_CHAT_CONNECT_TIMEOUT', 3),
12 + 'retry_times' => env('DAITO_GOOGLE_CHAT_RETRY_TIMES', 1),
13 + 'retry_sleep_ms' => env('DAITO_GOOGLE_CHAT_RETRY_SLEEP_MS', 200),
14 + 'queue_connection' => env('DAITO_GOOGLE_CHAT_QUEUE_CONNECTION', null),
15 + 'queue_name' => env('DAITO_GOOGLE_CHAT_QUEUE_NAME', null),
16 + 'queue_tries' => env('DAITO_GOOGLE_CHAT_QUEUE_TRIES', 3),
17 + 'queue_backoff_seconds' => env('DAITO_GOOGLE_CHAT_QUEUE_BACKOFF', 10),
18 + 'throw_on_error' => env('DAITO_GOOGLE_CHAT_THROW_ON_ERROR', false),
19 +);