|
78807
|
2764
|
75
|
2026-05-27T13:00:21.830462+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886821830_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.38730052,"top":0.10055866,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","depth":4,"bounds":{"left":0.13896276,"top":0.0,"width":0.27227393,"height":1.0},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
4833674345888640236
|
-1317633669888638642
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78806
|
NULL
|
NULL
|
NULL
|
|
78812
|
2765
|
1
|
2026-05-27T13:00:25.584254+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886825584_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
[2026-05-27 13:00:22] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-27 13:00:22] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}","depth":4,"on_screen":true,"value":"[2026-05-27 13:00:22] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-2914464536471357698
|
-1605866245084462770
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
[2026-05-27 13:00:22] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78810
|
NULL
|
NULL
|
NULL
|
|
78813
|
2766
|
1
|
2026-05-27T13:00:28.482144+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886828482_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:00:22] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.38730052,"top":0.10055866,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","depth":4,"bounds":{"left":0.13896276,"top":0.0,"width":0.27227393,"height":1.0},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.7293883,"top":0.10055866,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.7390292,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.7463431,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-27 13:00:22] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"value":"[2026-05-27 13:00:22] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8176311413163655699
|
-1605866245084462770
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:00:22] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78814
|
2765
|
2
|
2026-05-27T13:00:32.578181+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886832578_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
26
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:00:06] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"meeting-bot:schedule-bot","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"a7b7e056-070c-4d11-8493-7cf038fd3c89","trace_id":"39d404af-bfd4-491f-82fa-04db40ce4e42"}
[2026-05-27 13:00:06] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"a7b7e056-070c-4d11-8493-7cf038fd3c89","trace_id":"39d404af-bfd4-491f-82fa-04db40ce4e42"}
[2026-05-27 13:00:06] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"meeting-bot:schedule-bot","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"a7b7e056-070c-4d11-8493-7cf038fd3c89","trace_id":"39d404af-bfd4-491f-82fa-04db40ce4e42"}
[2026-05-27 13:00:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8bee13e2-b202-4001-babc-53a311722510","trace_id":"d4449447-a000-451d-b138-40245e2ff641"}
[2026-05-27 13:00:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8bee13e2-b202-4001-babc-53a311722510","trace_id":"d4449447-a000-451d-b138-40245e2ff641"}
[2026-05-27 13:00:10] local.NOTICE: Monitoring start {"correlation_id":"b73f0a3d-86b4-44ae-abb4-c78ffd8a62d5","trace_id":"c3fb479f-ae06-450e-97cb-30c62978c2a3"}
[2026-05-27 13:00:11] local.NOTICE: Monitoring end {"correlation_id":"b73f0a3d-86b4-44ae-abb4-c78ffd8a62d5","trace_id":"c3fb479f-ae06-450e-97cb-30c62978c2a3"}
[2026-05-27 13:00:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b626d4a6-a468-4c39-a1fb-df877b7a3bce","trace_id":"d8f56e4b-df24-4586-9a8c-6aac54196fa6"}
[2026-05-27 13:00:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"b626d4a6-a468-4c39-a1fb-df877b7a3bce","trace_id":"d8f56e4b-df24-4586-9a8c-6aac54196fa6"}
[2026-05-27 13:00:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"30132703-38c7-4e86-801d-7b313842cc2e","trace_id":"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c"}
[2026-05-27 13:00:15] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"30132703-38c7-4e86-801d-7b313842cc2e","trace_id":"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c"}
[2026-05-27 13:00:15] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"30132703-38c7-4e86-801d-7b313842cc2e","trace_id":"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c"}
[2026-05-27 13:00:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"30132703-38c7-4e86-801d-7b313842cc2e","trace_id":"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c"}
[2026-05-27 13:00:17] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"d1dce36a-d667-4aed-94ea-f8d171b4c192","trace_id":"24a11a52-454f-4af7-8c1a-5de787e5ad7d"}
[2026-05-27 13:00:17] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:58:00, 2026-05-27 13:00:00] {"correlation_id":"d1dce36a-d667-4aed-94ea-f8d171b4c192","trace_id":"24a11a52-454f-4af7-8c1a-5de787e5ad7d"}
[2026-05-27 13:00:17] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:58:00, 2026-05-27 13:00:00] {"correlation_id":"d1dce36a-d667-4aed-94ea-f8d171b4c192","trace_id":"24a11a52-454f-4af7-8c1a-5de787e5ad7d"}
[2026-05-27 13:00:17] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"d1dce36a-d667-4aed-94ea-f8d171b4c192","trace_id":"24a11a52-454f-4af7-8c1a-5de787e5ad7d"}
[2026-05-27 13:00:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"bd955878-5221-4d7c-b707-8bcbf085a399","trace_id":"a13080cd-0f14-43f9-be78-828f7ab0f60e"}
[2026-05-27 13:00:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"bd955878-5221-4d7c-b707-8bcbf085a399","trace_id":"a13080cd-0f14-43f9-be78-828f7ab0f60e"}
[2026-05-27 13:00:21] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:text-relay:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:22] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:22] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:text-relay:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:text-relay:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:text-relay:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:25] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:pre-meeting-notification","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"c954802d-4d49-42a9-a336-468cd681f94c","trace_id":"da7eae8b-1ee9-4936-b978-31d49d154428"}
[2026-05-27 13:00:25] local.INFO: Running pre-meeting notification command {"correlation_id":"c954802d-4d49-42a9-a336-468cd681f94c","trace_id":"da7eae8b-1ee9-4936-b978-31d49d154428"}
[2026-05-27 13:00:25] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:pre-meeting-notification","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"c954802d-4d49-42a9-a336-468cd681f94c","trace_id":"da7eae8b-1ee9-4936-b978-31d49d154428"}
[2026-05-27 13:00:27] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"0c9978ab-c277-4847-b907-d43aefd6e2b2","trace_id":"73ac0e3e-14e0-405c-87d4-146d6ea78f7a"}
[2026-05-27 13:00:27] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:50:00, 2026-05-27 12:55:00] {"correlation_id":"0c9978ab-c277-4847-b907-d43aefd6e2b2","trace_id":"73ac0e3e-14e0-405c-87d4-146d6ea78f7a"}
[2026-05-27 13:00:27] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:50:00, 2026-05-27 12:55:00] {"correlation_id":"0c9978ab-c277-4847-b907-d43aefd6e2b2","trace_id":"73ac0e3e-14e0-405c-87d4-146d6ea78f7a"}
[2026-05-27 13:00:27] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"0c9978ab-c277-4847-b907-d43aefd6e2b2","trace_id":"73ac0e3e-14e0-405c-87d4-146d6ea78f7a"}
[2026-05-27 13:00:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3","trace_id":"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f"}
[2026-05-27 13:00:28] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesEnded {"from":"12:55","to":"13:00"} {"correlation_id":"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3","trace_id":"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f"}
[2026-05-27 13:00:28] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {"from":"02:50","to":"02:55"} {"correlation_id":"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3","trace_id":"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f"}
[2026-05-27 13:00:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3","trace_id":"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f"}
[2026-05-27 13:00:30] local.NOTICE: Repairing HubSpot tokens start {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:30] local.INFO: Trying to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:30] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:30] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":59,"provider":"hubspot","refreshToken":"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b","state":"full-refresh"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.ERROR: Failed to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05","reason":"missing or invalid refresh token","previous":""} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: Trying to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":306,"provider":"hubspot","refreshToken":"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b","state":"full-refresh"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.ERROR: Failed to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03","reason":"missing or invalid refresh token","previous":""} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: Trying to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1372,"provider":"hubspot","refreshToken":"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4","state":"full-refresh"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:32] local.ERROR: Failed to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06","reason":"missing or invalid refresh token","previous":""} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:32] local.NOTICE: Repairing HubSpot tokens end {"total":3,"fixed":0,"failed":3} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"26","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-27 13:00:06] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"meeting-bot:schedule-bot\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"a7b7e056-070c-4d11-8493-7cf038fd3c89\",\"trace_id\":\"39d404af-bfd4-491f-82fa-04db40ce4e42\"}\n[2026-05-27 13:00:06] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"a7b7e056-070c-4d11-8493-7cf038fd3c89\",\"trace_id\":\"39d404af-bfd4-491f-82fa-04db40ce4e42\"}\n[2026-05-27 13:00:06] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"meeting-bot:schedule-bot\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"a7b7e056-070c-4d11-8493-7cf038fd3c89\",\"trace_id\":\"39d404af-bfd4-491f-82fa-04db40ce4e42\"}\n[2026-05-27 13:00:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8bee13e2-b202-4001-babc-53a311722510\",\"trace_id\":\"d4449447-a000-451d-b138-40245e2ff641\"}\n[2026-05-27 13:00:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8bee13e2-b202-4001-babc-53a311722510\",\"trace_id\":\"d4449447-a000-451d-b138-40245e2ff641\"}\n[2026-05-27 13:00:10] local.NOTICE: Monitoring start {\"correlation_id\":\"b73f0a3d-86b4-44ae-abb4-c78ffd8a62d5\",\"trace_id\":\"c3fb479f-ae06-450e-97cb-30c62978c2a3\"}\n[2026-05-27 13:00:11] local.NOTICE: Monitoring end {\"correlation_id\":\"b73f0a3d-86b4-44ae-abb4-c78ffd8a62d5\",\"trace_id\":\"c3fb479f-ae06-450e-97cb-30c62978c2a3\"}\n[2026-05-27 13:00:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b626d4a6-a468-4c39-a1fb-df877b7a3bce\",\"trace_id\":\"d8f56e4b-df24-4586-9a8c-6aac54196fa6\"}\n[2026-05-27 13:00:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"b626d4a6-a468-4c39-a1fb-df877b7a3bce\",\"trace_id\":\"d8f56e4b-df24-4586-9a8c-6aac54196fa6\"}\n[2026-05-27 13:00:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"30132703-38c7-4e86-801d-7b313842cc2e\",\"trace_id\":\"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c\"}\n[2026-05-27 13:00:15] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"30132703-38c7-4e86-801d-7b313842cc2e\",\"trace_id\":\"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c\"}\n[2026-05-27 13:00:15] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"30132703-38c7-4e86-801d-7b313842cc2e\",\"trace_id\":\"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c\"}\n[2026-05-27 13:00:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"30132703-38c7-4e86-801d-7b313842cc2e\",\"trace_id\":\"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c\"}\n[2026-05-27 13:00:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"d1dce36a-d667-4aed-94ea-f8d171b4c192\",\"trace_id\":\"24a11a52-454f-4af7-8c1a-5de787e5ad7d\"}\n[2026-05-27 13:00:17] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:58:00, 2026-05-27 13:00:00] {\"correlation_id\":\"d1dce36a-d667-4aed-94ea-f8d171b4c192\",\"trace_id\":\"24a11a52-454f-4af7-8c1a-5de787e5ad7d\"}\n[2026-05-27 13:00:17] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:58:00, 2026-05-27 13:00:00] {\"correlation_id\":\"d1dce36a-d667-4aed-94ea-f8d171b4c192\",\"trace_id\":\"24a11a52-454f-4af7-8c1a-5de787e5ad7d\"}\n[2026-05-27 13:00:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"d1dce36a-d667-4aed-94ea-f8d171b4c192\",\"trace_id\":\"24a11a52-454f-4af7-8c1a-5de787e5ad7d\"}\n[2026-05-27 13:00:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"bd955878-5221-4d7c-b707-8bcbf085a399\",\"trace_id\":\"a13080cd-0f14-43f9-be78-828f7ab0f60e\"}\n[2026-05-27 13:00:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"bd955878-5221-4d7c-b707-8bcbf085a399\",\"trace_id\":\"a13080cd-0f14-43f9-be78-828f7ab0f60e\"}\n[2026-05-27 13:00:21] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:22] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:22] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:25] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"c954802d-4d49-42a9-a336-468cd681f94c\",\"trace_id\":\"da7eae8b-1ee9-4936-b978-31d49d154428\"}\n[2026-05-27 13:00:25] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"c954802d-4d49-42a9-a336-468cd681f94c\",\"trace_id\":\"da7eae8b-1ee9-4936-b978-31d49d154428\"}\n[2026-05-27 13:00:25] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"c954802d-4d49-42a9-a336-468cd681f94c\",\"trace_id\":\"da7eae8b-1ee9-4936-b978-31d49d154428\"}\n[2026-05-27 13:00:27] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"0c9978ab-c277-4847-b907-d43aefd6e2b2\",\"trace_id\":\"73ac0e3e-14e0-405c-87d4-146d6ea78f7a\"}\n[2026-05-27 13:00:27] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:50:00, 2026-05-27 12:55:00] {\"correlation_id\":\"0c9978ab-c277-4847-b907-d43aefd6e2b2\",\"trace_id\":\"73ac0e3e-14e0-405c-87d4-146d6ea78f7a\"}\n[2026-05-27 13:00:27] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:50:00, 2026-05-27 12:55:00] {\"correlation_id\":\"0c9978ab-c277-4847-b907-d43aefd6e2b2\",\"trace_id\":\"73ac0e3e-14e0-405c-87d4-146d6ea78f7a\"}\n[2026-05-27 13:00:27] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"0c9978ab-c277-4847-b907-d43aefd6e2b2\",\"trace_id\":\"73ac0e3e-14e0-405c-87d4-146d6ea78f7a\"}\n[2026-05-27 13:00:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3\",\"trace_id\":\"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f\"}\n[2026-05-27 13:00:28] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"12:55\",\"to\":\"13:00\"} {\"correlation_id\":\"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3\",\"trace_id\":\"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f\"}\n[2026-05-27 13:00:28] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"02:50\",\"to\":\"02:55\"} {\"correlation_id\":\"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3\",\"trace_id\":\"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f\"}\n[2026-05-27 13:00:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3\",\"trace_id\":\"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f\"}\n[2026-05-27 13:00:30] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:30] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:30] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:30] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.ERROR: Failed to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.ERROR: Failed to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:32] local.ERROR: Failed to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:32] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}","depth":4,"on_screen":true,"value":"[2026-05-27 13:00:06] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"meeting-bot:schedule-bot\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"a7b7e056-070c-4d11-8493-7cf038fd3c89\",\"trace_id\":\"39d404af-bfd4-491f-82fa-04db40ce4e42\"}\n[2026-05-27 13:00:06] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"a7b7e056-070c-4d11-8493-7cf038fd3c89\",\"trace_id\":\"39d404af-bfd4-491f-82fa-04db40ce4e42\"}\n[2026-05-27 13:00:06] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"meeting-bot:schedule-bot\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"a7b7e056-070c-4d11-8493-7cf038fd3c89\",\"trace_id\":\"39d404af-bfd4-491f-82fa-04db40ce4e42\"}\n[2026-05-27 13:00:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8bee13e2-b202-4001-babc-53a311722510\",\"trace_id\":\"d4449447-a000-451d-b138-40245e2ff641\"}\n[2026-05-27 13:00:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8bee13e2-b202-4001-babc-53a311722510\",\"trace_id\":\"d4449447-a000-451d-b138-40245e2ff641\"}\n[2026-05-27 13:00:10] local.NOTICE: Monitoring start {\"correlation_id\":\"b73f0a3d-86b4-44ae-abb4-c78ffd8a62d5\",\"trace_id\":\"c3fb479f-ae06-450e-97cb-30c62978c2a3\"}\n[2026-05-27 13:00:11] local.NOTICE: Monitoring end {\"correlation_id\":\"b73f0a3d-86b4-44ae-abb4-c78ffd8a62d5\",\"trace_id\":\"c3fb479f-ae06-450e-97cb-30c62978c2a3\"}\n[2026-05-27 13:00:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b626d4a6-a468-4c39-a1fb-df877b7a3bce\",\"trace_id\":\"d8f56e4b-df24-4586-9a8c-6aac54196fa6\"}\n[2026-05-27 13:00:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"b626d4a6-a468-4c39-a1fb-df877b7a3bce\",\"trace_id\":\"d8f56e4b-df24-4586-9a8c-6aac54196fa6\"}\n[2026-05-27 13:00:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"30132703-38c7-4e86-801d-7b313842cc2e\",\"trace_id\":\"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c\"}\n[2026-05-27 13:00:15] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"30132703-38c7-4e86-801d-7b313842cc2e\",\"trace_id\":\"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c\"}\n[2026-05-27 13:00:15] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"30132703-38c7-4e86-801d-7b313842cc2e\",\"trace_id\":\"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c\"}\n[2026-05-27 13:00:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"30132703-38c7-4e86-801d-7b313842cc2e\",\"trace_id\":\"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c\"}\n[2026-05-27 13:00:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"d1dce36a-d667-4aed-94ea-f8d171b4c192\",\"trace_id\":\"24a11a52-454f-4af7-8c1a-5de787e5ad7d\"}\n[2026-05-27 13:00:17] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:58:00, 2026-05-27 13:00:00] {\"correlation_id\":\"d1dce36a-d667-4aed-94ea-f8d171b4c192\",\"trace_id\":\"24a11a52-454f-4af7-8c1a-5de787e5ad7d\"}\n[2026-05-27 13:00:17] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:58:00, 2026-05-27 13:00:00] {\"correlation_id\":\"d1dce36a-d667-4aed-94ea-f8d171b4c192\",\"trace_id\":\"24a11a52-454f-4af7-8c1a-5de787e5ad7d\"}\n[2026-05-27 13:00:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"d1dce36a-d667-4aed-94ea-f8d171b4c192\",\"trace_id\":\"24a11a52-454f-4af7-8c1a-5de787e5ad7d\"}\n[2026-05-27 13:00:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"bd955878-5221-4d7c-b707-8bcbf085a399\",\"trace_id\":\"a13080cd-0f14-43f9-be78-828f7ab0f60e\"}\n[2026-05-27 13:00:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"bd955878-5221-4d7c-b707-8bcbf085a399\",\"trace_id\":\"a13080cd-0f14-43f9-be78-828f7ab0f60e\"}\n[2026-05-27 13:00:21] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:22] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:22] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:25] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"c954802d-4d49-42a9-a336-468cd681f94c\",\"trace_id\":\"da7eae8b-1ee9-4936-b978-31d49d154428\"}\n[2026-05-27 13:00:25] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"c954802d-4d49-42a9-a336-468cd681f94c\",\"trace_id\":\"da7eae8b-1ee9-4936-b978-31d49d154428\"}\n[2026-05-27 13:00:25] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"c954802d-4d49-42a9-a336-468cd681f94c\",\"trace_id\":\"da7eae8b-1ee9-4936-b978-31d49d154428\"}\n[2026-05-27 13:00:27] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"0c9978ab-c277-4847-b907-d43aefd6e2b2\",\"trace_id\":\"73ac0e3e-14e0-405c-87d4-146d6ea78f7a\"}\n[2026-05-27 13:00:27] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:50:00, 2026-05-27 12:55:00] {\"correlation_id\":\"0c9978ab-c277-4847-b907-d43aefd6e2b2\",\"trace_id\":\"73ac0e3e-14e0-405c-87d4-146d6ea78f7a\"}\n[2026-05-27 13:00:27] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:50:00, 2026-05-27 12:55:00] {\"correlation_id\":\"0c9978ab-c277-4847-b907-d43aefd6e2b2\",\"trace_id\":\"73ac0e3e-14e0-405c-87d4-146d6ea78f7a\"}\n[2026-05-27 13:00:27] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"0c9978ab-c277-4847-b907-d43aefd6e2b2\",\"trace_id\":\"73ac0e3e-14e0-405c-87d4-146d6ea78f7a\"}\n[2026-05-27 13:00:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3\",\"trace_id\":\"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f\"}\n[2026-05-27 13:00:28] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"12:55\",\"to\":\"13:00\"} {\"correlation_id\":\"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3\",\"trace_id\":\"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f\"}\n[2026-05-27 13:00:28] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"02:50\",\"to\":\"02:55\"} {\"correlation_id\":\"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3\",\"trace_id\":\"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f\"}\n[2026-05-27 13:00:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3\",\"trace_id\":\"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f\"}\n[2026-05-27 13:00:30] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:30] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:30] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:30] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.ERROR: Failed to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.ERROR: Failed to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:31] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:32] local.ERROR: Failed to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}\n[2026-05-27 13:00:32] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"918b2501-c355-4d88-81b1-9194c1318254\",\"trace_id\":\"5ff699f0-053f-497b-89fd-98e7996fe200\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
6429875173604085361
|
-2511518051505332849
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
26
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:00:06] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"meeting-bot:schedule-bot","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"a7b7e056-070c-4d11-8493-7cf038fd3c89","trace_id":"39d404af-bfd4-491f-82fa-04db40ce4e42"}
[2026-05-27 13:00:06] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"a7b7e056-070c-4d11-8493-7cf038fd3c89","trace_id":"39d404af-bfd4-491f-82fa-04db40ce4e42"}
[2026-05-27 13:00:06] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"meeting-bot:schedule-bot","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"a7b7e056-070c-4d11-8493-7cf038fd3c89","trace_id":"39d404af-bfd4-491f-82fa-04db40ce4e42"}
[2026-05-27 13:00:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8bee13e2-b202-4001-babc-53a311722510","trace_id":"d4449447-a000-451d-b138-40245e2ff641"}
[2026-05-27 13:00:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8bee13e2-b202-4001-babc-53a311722510","trace_id":"d4449447-a000-451d-b138-40245e2ff641"}
[2026-05-27 13:00:10] local.NOTICE: Monitoring start {"correlation_id":"b73f0a3d-86b4-44ae-abb4-c78ffd8a62d5","trace_id":"c3fb479f-ae06-450e-97cb-30c62978c2a3"}
[2026-05-27 13:00:11] local.NOTICE: Monitoring end {"correlation_id":"b73f0a3d-86b4-44ae-abb4-c78ffd8a62d5","trace_id":"c3fb479f-ae06-450e-97cb-30c62978c2a3"}
[2026-05-27 13:00:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b626d4a6-a468-4c39-a1fb-df877b7a3bce","trace_id":"d8f56e4b-df24-4586-9a8c-6aac54196fa6"}
[2026-05-27 13:00:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"b626d4a6-a468-4c39-a1fb-df877b7a3bce","trace_id":"d8f56e4b-df24-4586-9a8c-6aac54196fa6"}
[2026-05-27 13:00:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"30132703-38c7-4e86-801d-7b313842cc2e","trace_id":"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c"}
[2026-05-27 13:00:15] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"30132703-38c7-4e86-801d-7b313842cc2e","trace_id":"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c"}
[2026-05-27 13:00:15] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"30132703-38c7-4e86-801d-7b313842cc2e","trace_id":"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c"}
[2026-05-27 13:00:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"30132703-38c7-4e86-801d-7b313842cc2e","trace_id":"fedbf6ce-fc49-49f2-b13e-786a1a1f5e9c"}
[2026-05-27 13:00:17] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"d1dce36a-d667-4aed-94ea-f8d171b4c192","trace_id":"24a11a52-454f-4af7-8c1a-5de787e5ad7d"}
[2026-05-27 13:00:17] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:58:00, 2026-05-27 13:00:00] {"correlation_id":"d1dce36a-d667-4aed-94ea-f8d171b4c192","trace_id":"24a11a52-454f-4af7-8c1a-5de787e5ad7d"}
[2026-05-27 13:00:17] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:58:00, 2026-05-27 13:00:00] {"correlation_id":"d1dce36a-d667-4aed-94ea-f8d171b4c192","trace_id":"24a11a52-454f-4af7-8c1a-5de787e5ad7d"}
[2026-05-27 13:00:17] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"d1dce36a-d667-4aed-94ea-f8d171b4c192","trace_id":"24a11a52-454f-4af7-8c1a-5de787e5ad7d"}
[2026-05-27 13:00:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"bd955878-5221-4d7c-b707-8bcbf085a399","trace_id":"a13080cd-0f14-43f9-be78-828f7ab0f60e"}
[2026-05-27 13:00:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"bd955878-5221-4d7c-b707-8bcbf085a399","trace_id":"a13080cd-0f14-43f9-be78-828f7ab0f60e"}
[2026-05-27 13:00:21] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:text-relay:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:22] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:22] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:text-relay:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:text-relay:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:text-relay:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:25] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:pre-meeting-notification","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"c954802d-4d49-42a9-a336-468cd681f94c","trace_id":"da7eae8b-1ee9-4936-b978-31d49d154428"}
[2026-05-27 13:00:25] local.INFO: Running pre-meeting notification command {"correlation_id":"c954802d-4d49-42a9-a336-468cd681f94c","trace_id":"da7eae8b-1ee9-4936-b978-31d49d154428"}
[2026-05-27 13:00:25] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:pre-meeting-notification","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"c954802d-4d49-42a9-a336-468cd681f94c","trace_id":"da7eae8b-1ee9-4936-b978-31d49d154428"}
[2026-05-27 13:00:27] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"0c9978ab-c277-4847-b907-d43aefd6e2b2","trace_id":"73ac0e3e-14e0-405c-87d4-146d6ea78f7a"}
[2026-05-27 13:00:27] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:50:00, 2026-05-27 12:55:00] {"correlation_id":"0c9978ab-c277-4847-b907-d43aefd6e2b2","trace_id":"73ac0e3e-14e0-405c-87d4-146d6ea78f7a"}
[2026-05-27 13:00:27] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:50:00, 2026-05-27 12:55:00] {"correlation_id":"0c9978ab-c277-4847-b907-d43aefd6e2b2","trace_id":"73ac0e3e-14e0-405c-87d4-146d6ea78f7a"}
[2026-05-27 13:00:27] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"0c9978ab-c277-4847-b907-d43aefd6e2b2","trace_id":"73ac0e3e-14e0-405c-87d4-146d6ea78f7a"}
[2026-05-27 13:00:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3","trace_id":"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f"}
[2026-05-27 13:00:28] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesEnded {"from":"12:55","to":"13:00"} {"correlation_id":"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3","trace_id":"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f"}
[2026-05-27 13:00:28] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {"from":"02:50","to":"02:55"} {"correlation_id":"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3","trace_id":"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f"}
[2026-05-27 13:00:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8dcac50d-ac12-4b4b-ab5c-50e0bc3a9ae3","trace_id":"4342ed1f-eb6f-4da9-aa1f-8a089d56c85f"}
[2026-05-27 13:00:30] local.NOTICE: Repairing HubSpot tokens start {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:30] local.INFO: Trying to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:30] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:30] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":59,"provider":"hubspot","refreshToken":"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b","state":"full-refresh"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.ERROR: Failed to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05","reason":"missing or invalid refresh token","previous":""} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: Trying to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":306,"provider":"hubspot","refreshToken":"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b","state":"full-refresh"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.ERROR: Failed to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03","reason":"missing or invalid refresh token","previous":""} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: Trying to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:31] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1372,"provider":"hubspot","refreshToken":"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4","state":"full-refresh"} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:32] local.ERROR: Failed to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06","reason":"missing or invalid refresh token","previous":""} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
[2026-05-27 13:00:32] local.NOTICE: Repairing HubSpot tokens end {"total":3,"fixed":0,"failed":3} {"correlation_id":"918b2501-c355-4d88-81b1-9194c1318254","trace_id":"5ff699f0-053f-497b-89fd-98e7996fe200"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78815
|
2766
|
2
|
2026-05-27T13:00:32.477333+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886832477_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-6107491181398235475
|
-8780371094093495853
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
rnpstomViewNeWENNCCoocTOOI-WindowFV faVsco.|s ~$ JY-20915-fix-missing-header-text-Proinet vSyncMailbox.phpxpplocos.onpp malionoppmaxio.oneppmep.onoprp media-steamino.onophe prophet-al.phpppqueue.onpppsalestorce.ongppsamiz.oneweesinoonopho webhook-sarver.ono>e contnt>M database>e doce>e tronteend>Elange node modules library root>BphpstanSemihiaS IMrocmirnochvimrmitosphp api.phpphp apl.v2.phpppconsole.onopp customer apronpphe embedded.phpwPneaith.ohpww scim.ohpwpupro.ectedweb.onoppweb.ohoppwebhook.ono>Mscriotey Mstomad→ demuanar>@ frameworkcitionoreOlaudio warWockhrhtaTextRelayServiceTest.phpcloss synchaztoox excenus codiane17 6124627 €KAK8&S8=hubsoot.icumal.oollod= laravel.logchounit.xmisltttis* evar scringrorecred Sszqnature = "a02lbox:text-relay:sync"?* The console connand descriozon* Evac stringnrorected Sdeseosntion "sune teyt relay"s nait hoy wth atest nessades"private SpelaySenw.ce:public function __construct(TextRelayService SrelayService)parent::_constructoSthis->relayService = SrelayService:public function handle@: voidtoy"I Tf we don't have relay confiquration, just bail nowcontad key:"jininny,googie texthost'") aa= null) ‹Snessagelds = Sthis->relayService-ssvnco)Sthis->uined strina:Rellaved, count emessaceds)" nessage(s)."D:carch Eycenton Sethssaserron a.scerlascade ooSantnus centucesycentcaiseSF fiminny@localhost) HSJocal (jiminny@alocalhost)& console [PROD)& console (EU)A console (STAGING2826-85-27 13:00:22) Local.INF0: SparanshistoryTypes => messageAddecstartHistoryId) => 35899correlatzon10: 05/44164-8662-4dda-a8cc-ec8c8a9c(2826-85-27 13:00:23) local.INF0: Sparans|*:"1cfab8b8-c292-4699-917c-2fbd65430125*}history voes = nessageaddedstar History d => 3582%correl arion id"so hosduadoisycardeoilygnrace iooiyue.ohuuno-osyeamersohdhaominooisokreieinomiaufts nessadetiston-trace".=1cfahRh8-6202-4600-017c-246665/39125*12826-85-27 13:00:2211 Jocnl TWEn: Snassadels stomEconnelattonSdw.:25620177.4054-1a47.8521-4031406283029 "6лася S/R.-R6874616.01/1-4662.0772-4138450hd620*300%L2•Wed 27 May 16:00:31TextRelayServiceTestner hensewiet+0.can I test it locally first. if I place env from stagingYes. The service relies on a tew thinas wou'd need in olacel1. Credentials file The constructor expects storage/text-relay.ison (a Google service account JSON key file)Gmbtrom sthaind and oince it there2. .env values to copy from stagingGoOGLE TEXTUSERECOOGLE TEXTI HASTGOOGLE TEXT RELAY TOPIC:DEPLOY REGION:the cnail nailbox address# e.g. txt.jiminny.com or txt.staging.jiminny.com# eu or us3. Run the sunc manualiv vin tinker tinside docker 1aeo 1%b bashaegdocker exec -it docker_lamp_1 php artisan tinkeapp(\Jiminny\Services\Ma1l\TextRelayService::class)-sync():Caveat: The service reads from Gmail history and dispatches jobs to the queue. If you don't want it to actuallsend SMS, make sure QUEUE_CONNECTICN»sync is not set (or use a test queue), or just call isForCurrentEnvironntioh.Ssvc = new class extends \Jiminny\Services\Mail\TextRelayService & public function _construct(som Serviceassvc-soctServicelcontiatainny.googletextI then sinsoect a eessade canual1sasg= sonailService-susers_messages-sget(config(*iminty.google text user'), *THE_MESSAGE ID*)collect(5-s0-20etPayload()-soetWeaders())-soluck("value"thut wey von can Mrity what handare tra nehhilm oraesot do ttat macesos alote tha ull evoc muneGadl -Ask anything (XOL)*« eodeAdhotvwodeurtate*4 space...
|
78813
|
NULL
|
NULL
|
NULL
|
|
78821
|
2765
|
5
|
2026-05-27T13:00:55.837288+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886855837_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:00:22] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-27 13:00:22] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}","depth":4,"on_screen":true,"value":"[2026-05-27 13:00:22] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8176311413163655699
|
-1605866245084462770
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:00:22] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78819
|
NULL
|
NULL
|
NULL
|
|
78822
|
2766
|
6
|
2026-05-27T13:00:59.188100+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886859188_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.38730052,"top":0.10055866,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","depth":4,"bounds":{"left":0.13896276,"top":0.0,"width":0.27227393,"height":1.0},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands\\Mailboxes\\TextRelay;\n\nuse Jiminny\\Console\\Commands\\Command;\nuse Jiminny\\Services\\Mail\\TextRelayService;\n\nclass SyncMailbox extends Command\n{\n /**\n * The name and signature of the console command.\n *\n * @var string\n */\n protected $signature = 'mailbox:text-relay:sync';\n\n /**\n * The console command description.\n *\n * @var string\n */\n protected $description = 'Sync text relay\\'s mailbox with latest messages.';\n private $relayService;\n\n public function __construct(TextRelayService $relayService)\n {\n parent::__construct();\n\n $this->relayService = $relayService;\n }\n\n /**\n * Execute the console command.\n *\n */\n public function handle(): void\n {\n try {\n // If we don't have relay configuration, just bail now.\n if (config('jiminny.google_text_host') === null) {\n return;\n }\n\n $messageIds = $this->relayService->sync();\n\n $this->line('Relayed ' . count($messageIds) . ' message(s).');\n } catch (\\Exception $e) {\n $this->error($e->getMessage());\n\n \\Sentry::captureException($e);\n }\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8362863801575691127
|
-1317650987187994298
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands\Mailboxes\TextRelay;
use Jiminny\Console\Commands\Command;
use Jiminny\Services\Mail\TextRelayService;
class SyncMailbox extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mailbox:text-relay:sync';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync text relay\'s mailbox with latest messages.';
private $relayService;
public function __construct(TextRelayService $relayService)
{
parent::__construct();
$this->relayService = $relayService;
}
/**
* Execute the console command.
*
*/
public function handle(): void
{
try {
// If we don't have relay configuration, just bail now.
if (config('jiminny.google_text_host') === null) {
return;
}
$messageIds = $this->relayService->sync();
$this->line('Relayed ' . count($messageIds) . ' message(s).');
} catch (\Exception $e) {
$this->error($e->getMessage());
\Sentry::captureException($e);
}
}
}...
|
78820
|
NULL
|
NULL
|
NULL
|
|
78829
|
2765
|
9
|
2026-05-27T13:01:40.202288+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886900202_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7923424182753190895
|
-8780372176425246254
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
iTerm2ShellEditViewSessionScriptsProfilesWindowHelp•• 0DOCKERO ₴1DEV (docker)-zsh*3eventsroutesviewsjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: stoppedworker-crm-update:worker-crm-update_00: stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3: jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00: stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00: stoppedworker-analytics:worker-analytics_00: stoppedworker-download:worker-download_00: stoppedworker:worker_00: stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker-audio:worker-audio_00: stoppedworker-calendar:worker-calendar_00: stoppedworker-conferences:worker-conferences_00:stoppedworker-crm-sync:worker-crm-sync_00: stoppedworker-emails:worker-emails_00: stoppedartisan-schedule:artisan-schedule_00: stoppedworker-es-update:worker-es-update_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2: jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00: startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00: startedworker-nudges:worker-nudges_00: startedrootedocker_lamp_1:/home/jiminny# php artisan mailbox:text-relay:syncRelayed 0 message(s).root@docker_lamp_1:/home/jiminny# php artisan mailbox:text-relay:syncRelayed ® message(s).root@docker_lamp_1:/home/jiminny# ]‹$0A100% C8• Wed 27 May 16:01:39DEV (docker)screenpipe"0 84-zshSprint Reviewnow - 4-4:30 PMCi Join Google Meet4.84ms DONEDEV...
|
78827
|
NULL
|
NULL
|
NULL
|
|
78832
|
2766
|
11
|
2026-05-27T13:01:43.252829+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886903252_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78833
|
2766
|
12
|
2026-05-27T13:01:45.110797+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886905110_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:00:22] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.7293883,"top":0.10055866,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.7390292,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.7463431,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-27 13:00:22] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"lines":[{"char_start":117,"char_count":2,"bounds":{"left":0.43051863,"top":0.0,"width":0.0026595744,"height":0.014365523}},{"char_start":119,"char_count":110,"bounds":{"left":0.43051863,"top":0.0,"width":0.28257978,"height":0.014365523}},{"char_start":229,"char_count":43,"bounds":{"left":0.43051863,"top":0.0,"width":0.1087101,"height":0.014365523}},{"char_start":272,"char_count":6,"bounds":{"left":0.43051863,"top":0.0,"width":0.012965426,"height":0.014365523}},{"char_start":278,"char_count":2,"bounds":{"left":0.43051863,"top":0.0,"width":0.0026595744,"height":0.014365523}},{"char_start":280,"char_count":35,"bounds":{"left":0.43051863,"top":0.0,"width":0.08809841,"height":0.014365523}},{"char_start":315,"char_count":31,"bounds":{"left":0.43051863,"top":0.008778931,"width":0.077792555,"height":0.014365523}},{"char_start":346,"char_count":2,"bounds":{"left":0.43051863,"top":0.026336791,"width":0.0026595744,"height":0.014365523}},{"char_start":348,"char_count":110,"bounds":{"left":0.43051863,"top":0.043894652,"width":0.28257978,"height":0.014365523}},{"char_start":458,"char_count":51,"bounds":{"left":0.43051863,"top":0.061452515,"width":0.12965426,"height":0.014365523}},{"char_start":509,"char_count":6,"bounds":{"left":0.43051863,"top":0.079010375,"width":0.012965426,"height":0.014365523}},{"char_start":515,"char_count":2,"bounds":{"left":0.43051863,"top":0.096568234,"width":0.0026595744,"height":0.014365523}},{"char_start":517,"char_count":2,"bounds":{"left":0.43051863,"top":0.11412609,"width":0.0026595744,"height":0.014365523}},{"char_start":519,"char_count":110,"bounds":{"left":0.43051863,"top":0.13168396,"width":0.28257978,"height":0.014365523}},{"char_start":629,"char_count":51,"bounds":{"left":0.43051863,"top":0.14924182,"width":0.12965426,"height":0.014365523}},{"char_start":680,"char_count":6,"bounds":{"left":0.43051863,"top":0.16679968,"width":0.012965426,"height":0.014365523}},{"char_start":686,"char_count":2,"bounds":{"left":0.43051863,"top":0.18435754,"width":0.0026595744,"height":0.014365523}},{"char_start":688,"char_count":2,"bounds":{"left":0.43051863,"top":0.2019154,"width":0.0026595744,"height":0.014365523}},{"char_start":690,"char_count":109,"bounds":{"left":0.43051863,"top":0.21947326,"width":0.28257978,"height":0.014365523}}],"value":"[2026-05-27 13:00:22] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8\",\"trace_id\":\"1cfab8b8-c292-4699-917c-2fbd65430125\"}\n[2026-05-27 13:00:23] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"25629d77-f95d-4a47-8521-a931d9c28322\",\"trace_id\":\"0c87f41c-9b41-4bb2-9332-f13ef59bdb49\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-2267235316267475140
|
-6434374886193984629
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:00:22] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d5219269-8c62-4dda-a8cc-ee0c0a9c1ae8","trace_id":"1cfab8b8-c292-4699-917c-2fbd65430125"}
[2026-05-27 13:00:23] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"25629d77-f95d-4a47-8521-a931d9c28322","trace_id":"0c87f41c-9b41-4bb2-9332-f13ef59bdb49"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78832
|
NULL
|
NULL
|
NULL
|
|
78834
|
2766
|
13
|
2026-05-27T13:01:48.208931+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886908208_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78835
|
2765
|
11
|
2026-05-27T13:02:13.516332+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886933516_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78831
|
NULL
|
NULL
|
NULL
|
|
78836
|
2766
|
14
|
2026-05-27T13:02:18.635003+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886938635_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78837
|
2765
|
12
|
2026-05-27T13:02:43.850399+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886963850_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78838
|
2766
|
15
|
2026-05-27T13:02:49.096455+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886969096_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78839
|
2765
|
13
|
2026-05-27T13:03:14.203312+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886994203_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78837
|
NULL
|
NULL
|
NULL
|
|
78840
|
2766
|
16
|
2026-05-27T13:03:19.537099+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886999537_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78841
|
2765
|
14
|
2026-05-27T13:03:44.513265+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887024513_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78837
|
NULL
|
NULL
|
NULL
|
|
78842
|
2766
|
17
|
2026-05-27T13:03:49.989041+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887029989_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78843
|
2765
|
15
|
2026-05-27T13:04:14.837705+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887054837_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78837
|
NULL
|
NULL
|
NULL
|
|
78844
|
2766
|
18
|
2026-05-27T13:04:20.402617+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887060402_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78845
|
2765
|
16
|
2026-05-27T13:04:45.199610+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887085199_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78837
|
NULL
|
NULL
|
NULL
|
|
78846
|
2766
|
19
|
2026-05-27T13:04:50.843151+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887090843_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78847
|
NULL
|
0
|
2026-05-27T13:05:15.584038+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887115584_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78837
|
NULL
|
NULL
|
NULL
|
|
78848
|
NULL
|
0
|
2026-05-27T13:05:21.269469+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887121269_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78849
|
2767
|
0
|
2026-05-27T13:05:45.918704+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887145918_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78837
|
NULL
|
NULL
|
NULL
|
|
78850
|
2768
|
0
|
2026-05-27T13:05:51.749+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887151749_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78851
|
2767
|
1
|
2026-05-27T13:06:16.253415+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887176253_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78837
|
NULL
|
NULL
|
NULL
|
|
78852
|
2768
|
1
|
2026-05-27T13:06:22.173319+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887182173_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78853
|
2767
|
2
|
2026-05-27T13:06:46.572682+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887206572_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78837
|
NULL
|
NULL
|
NULL
|
|
78854
|
2768
|
2
|
2026-05-27T13:06:52.629159+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887212629_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78855
|
2767
|
3
|
2026-05-27T13:07:16.977299+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887236977_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78837
|
NULL
|
NULL
|
NULL
|
|
78856
|
2768
|
3
|
2026-05-27T13:07:23.145441+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887243145_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78857
|
2767
|
4
|
2026-05-27T13:07:47.391225+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887267391_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78837
|
NULL
|
NULL
|
NULL
|
|
78858
|
NULL
|
0
|
2026-05-27T13:07:53.677055+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887273677_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
78859
|
NULL
|
0
|
2026-05-27T13:08:17.807723+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779887297807_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78837
|
NULL
|
NULL
|
NULL
|
|
78860
|
2770
|
0
|
2026-05-27T16:19:10.441159+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779898750441_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
20
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:05:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:05:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:15:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:15:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:45:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:50:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 13:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 14:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:25:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:25:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:40:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 14:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 15:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:00:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:45:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:55:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 15:55:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 16:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:10:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:15:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
[2026-05-27 16:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"20","depth":4,"bounds":{"left":0.72706115,"top":0.10055866,"width":0.010305851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.7390292,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.7463431,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-27 13:05:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:05:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:15:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:15:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:45:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:50:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 13:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 14:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:25:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:25:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:40:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 14:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 15:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:00:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:45:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:55:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 15:55:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 16:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:10:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:15:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}\n[2026-05-27 16:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}","depth":4,"bounds":{"left":0.4331782,"top":0.09736632,"width":0.32014626,"height":0.90263367},"on_screen":true,"value":"[2026-05-27 13:05:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:05:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:15:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:15:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:45:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:50:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 13:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 14:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:25:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:25:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:40:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 14:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 15:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:00:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:45:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:55:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 15:55:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 16:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:10:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:15:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}\n[2026-05-27 16:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8041996299966801941
|
-9137073216937041456
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
20
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:05:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:05:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:15:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:15:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:45:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:50:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 13:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 14:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:25:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:25:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:40:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 14:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 15:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:00:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:45:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:55:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 15:55:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 16:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:10:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:15:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
[2026-05-27 16:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78834
|
NULL
|
NULL
|
NULL
|
|
79077
|
2779
|
13
|
2026-05-28T06:17:27.904724+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779949047904_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
70
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:05:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:05:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:15:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:15:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:45:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:50:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 13:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 14:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:25:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:25:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:40:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 14:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 15:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:00:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:45:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:55:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 15:55:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 16:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:10:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:15:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
[2026-05-27 16:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
[2026-05-27 16:20:13] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2428f3a1-ffe7-47f6-9daa-3b37f277efe3","trace_id":"273bfee4-22a7-4ae9-85be-912f0f7d5ae5"}
[2026-05-27 16:20:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2428f3a1-ffe7-47f6-9daa-3b37f277efe3","trace_id":"273bfee4-22a7-4ae9-85be-912f0f7d5ae5"}
[2026-05-27 16:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0af368dd-50a6-4ae1-b384-a47434232748","trace_id":"a50d2d41-72b2-44b9-8c21-da2fe7cd2350"}
[2026-05-27 16:25:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0af368dd-50a6-4ae1-b384-a47434232748","trace_id":"a50d2d41-72b2-44b9-8c21-da2fe7cd2350"}
[2026-05-27 16:30:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7d75d5d3-7f4e-480b-beff-573be45c7d53","trace_id":"844a2d8a-624f-442a-9354-e4fcef280e0c"}
[2026-05-27 16:30:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7d75d5d3-7f4e-480b-beff-573be45c7d53","trace_id":"844a2d8a-624f-442a-9354-e4fcef280e0c"}
[2026-05-27 16:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"6d53364f-05f6-4d96-aac6-b1aab4359504","trace_id":"48efd308-178d-41ad-b1b2-1a09bc49ab01"}
[2026-05-27 16:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"6d53364f-05f6-4d96-aac6-b1aab4359504","trace_id":"48efd308-178d-41ad-b1b2-1a09bc49ab01"}
[2026-05-27 16:40:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"261d2acb-2d4e-47bd-a6cc-baefeb5675c4","trace_id":"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0"}
[2026-05-27 16:40:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"261d2acb-2d4e-47bd-a6cc-baefeb5675c4","trace_id":"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0"}
[2026-05-27 16:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b758c907-bbea-4bda-8f8d-028da9da8ce4","trace_id":"655a6ccc-e93f-4757-8abc-e6f6dd1f6755"}
[2026-05-27 16:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b758c907-bbea-4bda-8f8d-028da9da8ce4","trace_id":"655a6ccc-e93f-4757-8abc-e6f6dd1f6755"}
[2026-05-27 16:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7a56f511-63a9-44f4-8917-be2add000b25","trace_id":"70a3f353-bd37-479f-a3a4-090724cd4899"}
[2026-05-27 16:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7a56f511-63a9-44f4-8917-be2add000b25","trace_id":"70a3f353-bd37-479f-a3a4-090724cd4899"}
[2026-05-27 16:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5fa5a4be-c55a-4614-95e7-f92676eaeb32","trace_id":"fcf37132-7a76-4dd4-8e90-ad50e74d299f"}
[2026-05-27 16:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5fa5a4be-c55a-4614-95e7-f92676eaeb32","trace_id":"fcf37132-7a76-4dd4-8e90-ad50e74d299f"}
[2026-05-27 17:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4fcee8ab-7c17-4474-9070-83132139cec5","trace_id":"29069de8-5869-4e80-8eea-609e70d99bee"}
[2026-05-27 17:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4fcee8ab-7c17-4474-9070-83132139cec5","trace_id":"29069de8-5869-4e80-8eea-609e70d99bee"}
[2026-05-27 17:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"537b7577-77ba-4896-ad4a-dcedd1877d3b","trace_id":"6d3ed1b0-6fcd-4128-a479-27658f328642"}
[2026-05-27 17:05:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"537b7577-77ba-4896-ad4a-dcedd1877d3b","trace_id":"6d3ed1b0-6fcd-4128-a479-27658f328642"}
[2026-05-27 17:10:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0704fdc6-7b13-48fc-8471-d5b02041e7a0","trace_id":"071d0b93-27b5-437b-bdce-747dc0b6e427"}
[2026-05-27 17:10:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0704fdc6-7b13-48fc-8471-d5b02041e7a0","trace_id":"071d0b93-27b5-437b-bdce-747dc0b6e427"}
[2026-05-27 17:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"15ebdda0-0fa5-41c3-a1f3-495298d02859","trace_id":"3ff4f628-3477-4362-8093-175b6655f290"}
[2026-05-27 17:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"15ebdda0-0fa5-41c3-a1f3-495298d02859","trace_id":"3ff4f628-3477-4362-8093-175b6655f290"}
[2026-05-27 17:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f","trace_id":"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6"}
[2026-05-27 17:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f","trace_id":"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6"}
[2026-05-27 17:25:15] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2dab25b9-1a2d-417d-a88c-b086be423fa9","trace_id":"e4339f70-a4e7-44ec-836b-a779aefbef3e"}
[2026-05-27 17:25:15] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2dab25b9-1a2d-417d-a88c-b086be423fa9","trace_id":"e4339f70-a4e7-44ec-836b-a779aefbef3e"}
[2026-05-27 17:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a","trace_id":"b80243e5-07bb-41e6-b3a1-26d27a4f13f0"}
[2026-05-27 17:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a","trace_id":"b80243e5-07bb-41e6-b3a1-26d27a4f13f0"}
[2026-05-27 17:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dddd5b95-8803-4a70-af03-8a7174ca4a2b","trace_id":"176bc409-1338-47d7-96bc-d6205480794d"}
[2026-05-27 17:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dddd5b95-8803-4a70-af03-8a7174ca4a2b","trace_id":"176bc409-1338-47d7-96bc-d6205480794d"}
[2026-05-27 17:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4771c3be-e28d-4547-bc94-e2285ba51620","trace_id":"3355584d-0daa-4a8a-b121-9482a0db6783"}
[2026-05-27 17:40:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4771c3be-e28d-4547-bc94-e2285ba51620","trace_id":"3355584d-0daa-4a8a-b121-9482a0db6783"}
[2026-05-27 17:45:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"78d44e98-0241-4e33-b3d0-6adfb2a368fb","trace_id":"465221db-a122-4ec4-9746-14e9fd0fa5fc"}
[2026-05-27 17:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"78d44e98-0241-4e33-b3d0-6adfb2a368fb","trace_id":"465221db-a122-4ec4-9746-14e9fd0fa5fc"}
[2026-05-27 17:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"1d211a89-6b11-4803-a6b7-2e24f6710a52","trace_id":"b24cc5f0-e0c7-4840-bd3a-0174203a2f72"}
[2026-05-27 17:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"1d211a89-6b11-4803-a6b7-2e24f6710a52","trace_id":"b24cc5f0-e0c7-4840-bd3a-0174203a2f72"}
[2026-05-27 17:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"28c49b47-30c8-4d9a-83cd-b7d31b14bb37","trace_id":"14d95738-e32a-4694-b2a8-8c327237940b"}
[2026-05-27 17:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"28c49b47-30c8-4d9a-83cd-b7d31b14bb37","trace_id":"14d95738-e32a-4694-b2a8-8c327237940b"}
[2026-05-27 18:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"42220f4c-481c-46bd-ae41-326184f16003","trace_id":"3f2ed406-60ed-4941-883b-cb8a456b1b10"}
[2026-05-27 18:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"42220f4c-481c-46bd-ae41-326184f16003","trace_id":"3f2ed406-60ed-4941-883b-cb8a456b1b10"}
[2026-05-27 18:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"93db33bd-d0bd-46ea-8030-ae24b36ae963","trace_id":"4696955a-81e1-4105-b8dd-61ff7a4f5584"}
[2026-05-27 18:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"93db33bd-d0bd-46ea-8030-ae24b36ae963","trace_id":"4696955a-81e1-4105-b8dd-61ff7a4f5584"}
[2026-05-27 18:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b0ede837-0cc5-479a-ab75-db5c4101476f","trace_id":"d7f0855f-051c-41e2-b67c-0afb6a0c294e"}
[2026-05-27 18:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b0ede837-0cc5-479a-ab75-db5c4101476f","trace_id":"d7f0855f-051c-41e2-b67c-0afb6a0c294e"}
[2026-05-27 18:15:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b1d06246-dd9a-482e-bca8-658da1b25c37","trace_id":"9146c9f3-e05f-489d-95f4-5f2c3b988440"}
[2026-05-27 18:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b1d06246-dd9a-482e-bca8-658da1b25c37","trace_id":"9146c9f3-e05f-489d-95f4-5f2c3b988440"}
[2026-05-27 18:20:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e2a28a86-e3c3-46c3-88c5-209198f50961","trace_id":"9e56205b-18a8-4a25-a181-183a9ffbf4d4"}
[2026-05-27 18:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e2a28a86-e3c3-46c3-88c5-209198f50961","trace_id":"9e56205b-18a8-4a25-a181-183a9ffbf4d4"}
[2026-05-27 18:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4926c519-81cb-435e-a7db-e0cf00a90c14","trace_id":"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c"}
[2026-05-27 18:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4926c519-81cb-435e-a7db-e0cf00a90c14","trace_id":"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c"}
[2026-05-27 18:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"93963a2f-85d9-4cc5-a249-82262b8edaf8","trace_id":"df8fcf0f-7260-45ec-ae3b-ed556bad8a69"}
[2026-05-27 18:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"93963a2f-85d9-4cc5-a249-82262b8edaf8","trace_id":"df8fcf0f-7260-45ec-ae3b-ed556bad8a69"}
[2026-05-27 18:35:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e99c406d-511d-4593-8539-0264e7b2172b","trace_id":"47f52e14-dd88-428e-86af-8b252ac156b6"}
[2026-05-27 18:35:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e99c406d-511d-4593-8539-0264e7b2172b","trace_id":"47f52e14-dd88-428e-86af-8b252ac156b6"}
[2026-05-27 18:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e6d0ee61-1297-496e-84a2-8b81855e39b3","trace_id":"90f74038-8fbd-4634-95d1-6b8f9eefd7b4"}
[2026-05-27 18:40:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e6d0ee61-1297-496e-84a2-8b81855e39b3","trace_id":"90f74038-8fbd-4634-95d1-6b8f9eefd7b4"}
[2026-05-27 18:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"75258aa9-6788-4051-963b-914c871bbc26","trace_id":"86b6f05b-9049-4e3c-a73e-6585c75750ae"}
[2026-05-27 18:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"75258aa9-6788-4051-963b-914c871bbc26","trace_id":"86b6f05b-9049-4e3c-a73e-6585c75750ae"}
[2026-05-27 18:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"881c5f9a-d1aa-40a1-bda0-090fed822fea","trace_id":"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924"}
[2026-05-27 18:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"881c5f9a-d1aa-40a1-bda0-090fed822fea","trace_id":"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924"}
[2026-05-27 18:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"991ee58d-b151-4462-bda3-94ca8d8aae80","trace_id":"6bbdfd15-e1f3-40da-8f33-3486b4fab01c"}
[2026-05-27 18:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"991ee58d-b151-4462-bda3-94ca8d8aae80","trace_id":"6bbdfd15-e1f3-40da-8f33-3486b4fab01c"}
[2026-05-27 22:30:28] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b435dbfd-b566-49d2-b863-abb69460900c","trace_id":"89070b16-0908-46c7-9804-46717c0893a2"}
[2026-05-27 22:30:29] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b435dbfd-b566-49d2-b863-abb69460900c","trace_id":"89070b16-0908-46c7-9804-46717c0893a2"}
[2026-05-28 02:35:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"[CREDIT_CARD]-bfe0-d66ab8141278","trace_id":"e624f869-6e34-4a09-9aa6-318387902ae0"}
[2026-05-28 02:35:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"[CREDIT_CARD]-bfe0-d66ab8141278","trace_id":"e624f869-6e34-4a09-9aa6-318387902ae0"}
[2026-05-28 03:40:19] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a","trace_id":"6718951a-b93d-4018-9306-1d2a9fc08947"}
[2026-05-28 03:40:20] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a","trace_id":"6718951a-b93d-4018-9306-1d2a9fc08947"}
[2026-05-28 05:15:19] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"40a9b437-3914-4fc9-bc30-0928dc6ca2e4","trace_id":"71233b87-f2d0-4aaa-9b89-4cf0bdab4416"}
[2026-05-28 05:15:19] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"40a9b437-3914-4fc9-bc30-0928dc6ca2e4","trace_id":"71233b87-f2d0-4aaa-9b89-4cf0bdab4416"}
[2026-05-28 05:45:32] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"df61a176-ebde-4287-a6b3-4c279ecbef90","trace_id":"16d667db-a08d-416f-a0b6-de6aa91d95e6"}
[2026-05-28 05:45:32] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"df61a176-ebde-4287-a6b3-4c279ecbef90","trace_id":"16d667db-a08d-416f-a0b6-de6aa91d95e6"}
[2026-05-28 05:50:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dc4ad47f-f741-4320-89f3-69916d6c0e48","trace_id":"ffcdcb94-a2d5-4277-add2-eb2449b5c82e"}
[2026-05-28 05:50:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dc4ad47f-f741-4320-89f3-69916d6c0e48","trace_id":"ffcdcb94-a2d5-4277-add2-eb2449b5c82e"}
[2026-05-28 05:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4be209e1-8831-49f4-8d94-62852512812a","trace_id":"a2e0926d-7628-43b2-9a24-68beddc58f0e"}
[2026-05-28 05:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4be209e1-8831-49f4-8d94-62852512812a","trace_id":"a2e0926d-7628-43b2-9a24-68beddc58f0e"}
[2026-05-28 06:00:13] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"42647b91-48cd-4506-9527-60b7dd707516","trace_id":"30f5a8fd-b99c-477f-b44c-6d13d2df15e4"}
[2026-05-28 06:00:16] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"42647b91-48cd-4506-9527-60b7dd707516","trace_id":"30f5a8fd-b99c-477f-b44c-6d13d2df15e4"}
[2026-05-28 06:05:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e98fd078-62a9-4496-bd8f-9a5b209b7938","trace_id":"f2c87487-7772-4543-b931-fdf7095a50b2"}
[2026-05-28 06:05:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e98fd078-62a9-4496-bd8f-9a5b209b7938","trace_id":"f2c87487-7772-4543-b931-fdf7095a50b2"}
[2026-05-28 06:10:16] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"92743ccb-6275-4aef-948a-962d2116f793","trace_id":"6ac60160-dc90-4b6c-8e90-2bcb842751d7"}
[2026-05-28 06:10:16] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"92743ccb-6275-4aef-948a-962d2116f793","trace_id":"6ac60160-dc90-4b6c-8e90-2bcb842751d7"}
[2026-05-28 06:15:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e36a54ec-35c2-4440-8fd3-b88b4690fcb8","trace_id":"d5e1a237-9248-4fde-a2a4-e24c61267db9"}
[2026-05-28 06:15:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e36a54ec-35c2-4440-8fd3-b88b4690fcb8","trace_id":"d5e1a237-9248-4fde-a2a4-e24c61267db9"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"70","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-27 13:05:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:05:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:15:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:15:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:45:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:50:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 13:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 14:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:25:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:25:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:40:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 14:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 15:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:00:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:45:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:55:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 15:55:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 16:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:10:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:15:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}\n[2026-05-27 16:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}\n[2026-05-27 16:20:13] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2428f3a1-ffe7-47f6-9daa-3b37f277efe3\",\"trace_id\":\"273bfee4-22a7-4ae9-85be-912f0f7d5ae5\"}\n[2026-05-27 16:20:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2428f3a1-ffe7-47f6-9daa-3b37f277efe3\",\"trace_id\":\"273bfee4-22a7-4ae9-85be-912f0f7d5ae5\"}\n[2026-05-27 16:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0af368dd-50a6-4ae1-b384-a47434232748\",\"trace_id\":\"a50d2d41-72b2-44b9-8c21-da2fe7cd2350\"}\n[2026-05-27 16:25:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0af368dd-50a6-4ae1-b384-a47434232748\",\"trace_id\":\"a50d2d41-72b2-44b9-8c21-da2fe7cd2350\"}\n[2026-05-27 16:30:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7d75d5d3-7f4e-480b-beff-573be45c7d53\",\"trace_id\":\"844a2d8a-624f-442a-9354-e4fcef280e0c\"}\n[2026-05-27 16:30:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7d75d5d3-7f4e-480b-beff-573be45c7d53\",\"trace_id\":\"844a2d8a-624f-442a-9354-e4fcef280e0c\"}\n[2026-05-27 16:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"6d53364f-05f6-4d96-aac6-b1aab4359504\",\"trace_id\":\"48efd308-178d-41ad-b1b2-1a09bc49ab01\"}\n[2026-05-27 16:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"6d53364f-05f6-4d96-aac6-b1aab4359504\",\"trace_id\":\"48efd308-178d-41ad-b1b2-1a09bc49ab01\"}\n[2026-05-27 16:40:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"261d2acb-2d4e-47bd-a6cc-baefeb5675c4\",\"trace_id\":\"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0\"}\n[2026-05-27 16:40:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"261d2acb-2d4e-47bd-a6cc-baefeb5675c4\",\"trace_id\":\"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0\"}\n[2026-05-27 16:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b758c907-bbea-4bda-8f8d-028da9da8ce4\",\"trace_id\":\"655a6ccc-e93f-4757-8abc-e6f6dd1f6755\"}\n[2026-05-27 16:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b758c907-bbea-4bda-8f8d-028da9da8ce4\",\"trace_id\":\"655a6ccc-e93f-4757-8abc-e6f6dd1f6755\"}\n[2026-05-27 16:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7a56f511-63a9-44f4-8917-be2add000b25\",\"trace_id\":\"70a3f353-bd37-479f-a3a4-090724cd4899\"}\n[2026-05-27 16:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7a56f511-63a9-44f4-8917-be2add000b25\",\"trace_id\":\"70a3f353-bd37-479f-a3a4-090724cd4899\"}\n[2026-05-27 16:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5fa5a4be-c55a-4614-95e7-f92676eaeb32\",\"trace_id\":\"fcf37132-7a76-4dd4-8e90-ad50e74d299f\"}\n[2026-05-27 16:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5fa5a4be-c55a-4614-95e7-f92676eaeb32\",\"trace_id\":\"fcf37132-7a76-4dd4-8e90-ad50e74d299f\"}\n[2026-05-27 17:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4fcee8ab-7c17-4474-9070-83132139cec5\",\"trace_id\":\"29069de8-5869-4e80-8eea-609e70d99bee\"}\n[2026-05-27 17:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4fcee8ab-7c17-4474-9070-83132139cec5\",\"trace_id\":\"29069de8-5869-4e80-8eea-609e70d99bee\"}\n[2026-05-27 17:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"537b7577-77ba-4896-ad4a-dcedd1877d3b\",\"trace_id\":\"6d3ed1b0-6fcd-4128-a479-27658f328642\"}\n[2026-05-27 17:05:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"537b7577-77ba-4896-ad4a-dcedd1877d3b\",\"trace_id\":\"6d3ed1b0-6fcd-4128-a479-27658f328642\"}\n[2026-05-27 17:10:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0704fdc6-7b13-48fc-8471-d5b02041e7a0\",\"trace_id\":\"071d0b93-27b5-437b-bdce-747dc0b6e427\"}\n[2026-05-27 17:10:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0704fdc6-7b13-48fc-8471-d5b02041e7a0\",\"trace_id\":\"071d0b93-27b5-437b-bdce-747dc0b6e427\"}\n[2026-05-27 17:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"15ebdda0-0fa5-41c3-a1f3-495298d02859\",\"trace_id\":\"3ff4f628-3477-4362-8093-175b6655f290\"}\n[2026-05-27 17:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"15ebdda0-0fa5-41c3-a1f3-495298d02859\",\"trace_id\":\"3ff4f628-3477-4362-8093-175b6655f290\"}\n[2026-05-27 17:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f\",\"trace_id\":\"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6\"}\n[2026-05-27 17:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f\",\"trace_id\":\"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6\"}\n[2026-05-27 17:25:15] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2dab25b9-1a2d-417d-a88c-b086be423fa9\",\"trace_id\":\"e4339f70-a4e7-44ec-836b-a779aefbef3e\"}\n[2026-05-27 17:25:15] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2dab25b9-1a2d-417d-a88c-b086be423fa9\",\"trace_id\":\"e4339f70-a4e7-44ec-836b-a779aefbef3e\"}\n[2026-05-27 17:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a\",\"trace_id\":\"b80243e5-07bb-41e6-b3a1-26d27a4f13f0\"}\n[2026-05-27 17:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a\",\"trace_id\":\"b80243e5-07bb-41e6-b3a1-26d27a4f13f0\"}\n[2026-05-27 17:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dddd5b95-8803-4a70-af03-8a7174ca4a2b\",\"trace_id\":\"176bc409-1338-47d7-96bc-d6205480794d\"}\n[2026-05-27 17:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dddd5b95-8803-4a70-af03-8a7174ca4a2b\",\"trace_id\":\"176bc409-1338-47d7-96bc-d6205480794d\"}\n[2026-05-27 17:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4771c3be-e28d-4547-bc94-e2285ba51620\",\"trace_id\":\"3355584d-0daa-4a8a-b121-9482a0db6783\"}\n[2026-05-27 17:40:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4771c3be-e28d-4547-bc94-e2285ba51620\",\"trace_id\":\"3355584d-0daa-4a8a-b121-9482a0db6783\"}\n[2026-05-27 17:45:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"78d44e98-0241-4e33-b3d0-6adfb2a368fb\",\"trace_id\":\"465221db-a122-4ec4-9746-14e9fd0fa5fc\"}\n[2026-05-27 17:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"78d44e98-0241-4e33-b3d0-6adfb2a368fb\",\"trace_id\":\"465221db-a122-4ec4-9746-14e9fd0fa5fc\"}\n[2026-05-27 17:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"1d211a89-6b11-4803-a6b7-2e24f6710a52\",\"trace_id\":\"b24cc5f0-e0c7-4840-bd3a-0174203a2f72\"}\n[2026-05-27 17:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"1d211a89-6b11-4803-a6b7-2e24f6710a52\",\"trace_id\":\"b24cc5f0-e0c7-4840-bd3a-0174203a2f72\"}\n[2026-05-27 17:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"28c49b47-30c8-4d9a-83cd-b7d31b14bb37\",\"trace_id\":\"14d95738-e32a-4694-b2a8-8c327237940b\"}\n[2026-05-27 17:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"28c49b47-30c8-4d9a-83cd-b7d31b14bb37\",\"trace_id\":\"14d95738-e32a-4694-b2a8-8c327237940b\"}\n[2026-05-27 18:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"42220f4c-481c-46bd-ae41-326184f16003\",\"trace_id\":\"3f2ed406-60ed-4941-883b-cb8a456b1b10\"}\n[2026-05-27 18:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"42220f4c-481c-46bd-ae41-326184f16003\",\"trace_id\":\"3f2ed406-60ed-4941-883b-cb8a456b1b10\"}\n[2026-05-27 18:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"93db33bd-d0bd-46ea-8030-ae24b36ae963\",\"trace_id\":\"4696955a-81e1-4105-b8dd-61ff7a4f5584\"}\n[2026-05-27 18:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"93db33bd-d0bd-46ea-8030-ae24b36ae963\",\"trace_id\":\"4696955a-81e1-4105-b8dd-61ff7a4f5584\"}\n[2026-05-27 18:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b0ede837-0cc5-479a-ab75-db5c4101476f\",\"trace_id\":\"d7f0855f-051c-41e2-b67c-0afb6a0c294e\"}\n[2026-05-27 18:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b0ede837-0cc5-479a-ab75-db5c4101476f\",\"trace_id\":\"d7f0855f-051c-41e2-b67c-0afb6a0c294e\"}\n[2026-05-27 18:15:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b1d06246-dd9a-482e-bca8-658da1b25c37\",\"trace_id\":\"9146c9f3-e05f-489d-95f4-5f2c3b988440\"}\n[2026-05-27 18:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b1d06246-dd9a-482e-bca8-658da1b25c37\",\"trace_id\":\"9146c9f3-e05f-489d-95f4-5f2c3b988440\"}\n[2026-05-27 18:20:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e2a28a86-e3c3-46c3-88c5-209198f50961\",\"trace_id\":\"9e56205b-18a8-4a25-a181-183a9ffbf4d4\"}\n[2026-05-27 18:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e2a28a86-e3c3-46c3-88c5-209198f50961\",\"trace_id\":\"9e56205b-18a8-4a25-a181-183a9ffbf4d4\"}\n[2026-05-27 18:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4926c519-81cb-435e-a7db-e0cf00a90c14\",\"trace_id\":\"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c\"}\n[2026-05-27 18:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4926c519-81cb-435e-a7db-e0cf00a90c14\",\"trace_id\":\"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c\"}\n[2026-05-27 18:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"93963a2f-85d9-4cc5-a249-82262b8edaf8\",\"trace_id\":\"df8fcf0f-7260-45ec-ae3b-ed556bad8a69\"}\n[2026-05-27 18:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"93963a2f-85d9-4cc5-a249-82262b8edaf8\",\"trace_id\":\"df8fcf0f-7260-45ec-ae3b-ed556bad8a69\"}\n[2026-05-27 18:35:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e99c406d-511d-4593-8539-0264e7b2172b\",\"trace_id\":\"47f52e14-dd88-428e-86af-8b252ac156b6\"}\n[2026-05-27 18:35:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e99c406d-511d-4593-8539-0264e7b2172b\",\"trace_id\":\"47f52e14-dd88-428e-86af-8b252ac156b6\"}\n[2026-05-27 18:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e6d0ee61-1297-496e-84a2-8b81855e39b3\",\"trace_id\":\"90f74038-8fbd-4634-95d1-6b8f9eefd7b4\"}\n[2026-05-27 18:40:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e6d0ee61-1297-496e-84a2-8b81855e39b3\",\"trace_id\":\"90f74038-8fbd-4634-95d1-6b8f9eefd7b4\"}\n[2026-05-27 18:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"75258aa9-6788-4051-963b-914c871bbc26\",\"trace_id\":\"86b6f05b-9049-4e3c-a73e-6585c75750ae\"}\n[2026-05-27 18:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"75258aa9-6788-4051-963b-914c871bbc26\",\"trace_id\":\"86b6f05b-9049-4e3c-a73e-6585c75750ae\"}\n[2026-05-27 18:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"881c5f9a-d1aa-40a1-bda0-090fed822fea\",\"trace_id\":\"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924\"}\n[2026-05-27 18:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"881c5f9a-d1aa-40a1-bda0-090fed822fea\",\"trace_id\":\"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924\"}\n[2026-05-27 18:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"991ee58d-b151-4462-bda3-94ca8d8aae80\",\"trace_id\":\"6bbdfd15-e1f3-40da-8f33-3486b4fab01c\"}\n[2026-05-27 18:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"991ee58d-b151-4462-bda3-94ca8d8aae80\",\"trace_id\":\"6bbdfd15-e1f3-40da-8f33-3486b4fab01c\"}\n[2026-05-27 22:30:28] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b435dbfd-b566-49d2-b863-abb69460900c\",\"trace_id\":\"89070b16-0908-46c7-9804-46717c0893a2\"}\n[2026-05-27 22:30:29] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b435dbfd-b566-49d2-b863-abb69460900c\",\"trace_id\":\"89070b16-0908-46c7-9804-46717c0893a2\"}\n[2026-05-28 02:35:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"54253196-1163-4487-bfe0-d66ab8141278\",\"trace_id\":\"e624f869-6e34-4a09-9aa6-318387902ae0\"}\n[2026-05-28 02:35:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"54253196-1163-4487-bfe0-d66ab8141278\",\"trace_id\":\"e624f869-6e34-4a09-9aa6-318387902ae0\"}\n[2026-05-28 03:40:19] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a\",\"trace_id\":\"6718951a-b93d-4018-9306-1d2a9fc08947\"}\n[2026-05-28 03:40:20] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a\",\"trace_id\":\"6718951a-b93d-4018-9306-1d2a9fc08947\"}\n[2026-05-28 05:15:19] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"40a9b437-3914-4fc9-bc30-0928dc6ca2e4\",\"trace_id\":\"71233b87-f2d0-4aaa-9b89-4cf0bdab4416\"}\n[2026-05-28 05:15:19] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"40a9b437-3914-4fc9-bc30-0928dc6ca2e4\",\"trace_id\":\"71233b87-f2d0-4aaa-9b89-4cf0bdab4416\"}\n[2026-05-28 05:45:32] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"df61a176-ebde-4287-a6b3-4c279ecbef90\",\"trace_id\":\"16d667db-a08d-416f-a0b6-de6aa91d95e6\"}\n[2026-05-28 05:45:32] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"df61a176-ebde-4287-a6b3-4c279ecbef90\",\"trace_id\":\"16d667db-a08d-416f-a0b6-de6aa91d95e6\"}\n[2026-05-28 05:50:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dc4ad47f-f741-4320-89f3-69916d6c0e48\",\"trace_id\":\"ffcdcb94-a2d5-4277-add2-eb2449b5c82e\"}\n[2026-05-28 05:50:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dc4ad47f-f741-4320-89f3-69916d6c0e48\",\"trace_id\":\"ffcdcb94-a2d5-4277-add2-eb2449b5c82e\"}\n[2026-05-28 05:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4be209e1-8831-49f4-8d94-62852512812a\",\"trace_id\":\"a2e0926d-7628-43b2-9a24-68beddc58f0e\"}\n[2026-05-28 05:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4be209e1-8831-49f4-8d94-62852512812a\",\"trace_id\":\"a2e0926d-7628-43b2-9a24-68beddc58f0e\"}\n[2026-05-28 06:00:13] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"42647b91-48cd-4506-9527-60b7dd707516\",\"trace_id\":\"30f5a8fd-b99c-477f-b44c-6d13d2df15e4\"}\n[2026-05-28 06:00:16] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"42647b91-48cd-4506-9527-60b7dd707516\",\"trace_id\":\"30f5a8fd-b99c-477f-b44c-6d13d2df15e4\"}\n[2026-05-28 06:05:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e98fd078-62a9-4496-bd8f-9a5b209b7938\",\"trace_id\":\"f2c87487-7772-4543-b931-fdf7095a50b2\"}\n[2026-05-28 06:05:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e98fd078-62a9-4496-bd8f-9a5b209b7938\",\"trace_id\":\"f2c87487-7772-4543-b931-fdf7095a50b2\"}\n[2026-05-28 06:10:16] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"92743ccb-6275-4aef-948a-962d2116f793\",\"trace_id\":\"6ac60160-dc90-4b6c-8e90-2bcb842751d7\"}\n[2026-05-28 06:10:16] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"92743ccb-6275-4aef-948a-962d2116f793\",\"trace_id\":\"6ac60160-dc90-4b6c-8e90-2bcb842751d7\"}\n[2026-05-28 06:15:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e36a54ec-35c2-4440-8fd3-b88b4690fcb8\",\"trace_id\":\"d5e1a237-9248-4fde-a2a4-e24c61267db9\"}\n[2026-05-28 06:15:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e36a54ec-35c2-4440-8fd3-b88b4690fcb8\",\"trace_id\":\"d5e1a237-9248-4fde-a2a4-e24c61267db9\"}","depth":4,"on_screen":true,"value":"[2026-05-27 13:05:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:05:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:15:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:15:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:45:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:50:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 13:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 14:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:25:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:25:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:40:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 14:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 15:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:00:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:45:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:55:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 15:55:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 16:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:10:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:15:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}\n[2026-05-27 16:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}\n[2026-05-27 16:20:13] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2428f3a1-ffe7-47f6-9daa-3b37f277efe3\",\"trace_id\":\"273bfee4-22a7-4ae9-85be-912f0f7d5ae5\"}\n[2026-05-27 16:20:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2428f3a1-ffe7-47f6-9daa-3b37f277efe3\",\"trace_id\":\"273bfee4-22a7-4ae9-85be-912f0f7d5ae5\"}\n[2026-05-27 16:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0af368dd-50a6-4ae1-b384-a47434232748\",\"trace_id\":\"a50d2d41-72b2-44b9-8c21-da2fe7cd2350\"}\n[2026-05-27 16:25:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0af368dd-50a6-4ae1-b384-a47434232748\",\"trace_id\":\"a50d2d41-72b2-44b9-8c21-da2fe7cd2350\"}\n[2026-05-27 16:30:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7d75d5d3-7f4e-480b-beff-573be45c7d53\",\"trace_id\":\"844a2d8a-624f-442a-9354-e4fcef280e0c\"}\n[2026-05-27 16:30:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7d75d5d3-7f4e-480b-beff-573be45c7d53\",\"trace_id\":\"844a2d8a-624f-442a-9354-e4fcef280e0c\"}\n[2026-05-27 16:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"6d53364f-05f6-4d96-aac6-b1aab4359504\",\"trace_id\":\"48efd308-178d-41ad-b1b2-1a09bc49ab01\"}\n[2026-05-27 16:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"6d53364f-05f6-4d96-aac6-b1aab4359504\",\"trace_id\":\"48efd308-178d-41ad-b1b2-1a09bc49ab01\"}\n[2026-05-27 16:40:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"261d2acb-2d4e-47bd-a6cc-baefeb5675c4\",\"trace_id\":\"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0\"}\n[2026-05-27 16:40:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"261d2acb-2d4e-47bd-a6cc-baefeb5675c4\",\"trace_id\":\"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0\"}\n[2026-05-27 16:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b758c907-bbea-4bda-8f8d-028da9da8ce4\",\"trace_id\":\"655a6ccc-e93f-4757-8abc-e6f6dd1f6755\"}\n[2026-05-27 16:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b758c907-bbea-4bda-8f8d-028da9da8ce4\",\"trace_id\":\"655a6ccc-e93f-4757-8abc-e6f6dd1f6755\"}\n[2026-05-27 16:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7a56f511-63a9-44f4-8917-be2add000b25\",\"trace_id\":\"70a3f353-bd37-479f-a3a4-090724cd4899\"}\n[2026-05-27 16:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7a56f511-63a9-44f4-8917-be2add000b25\",\"trace_id\":\"70a3f353-bd37-479f-a3a4-090724cd4899\"}\n[2026-05-27 16:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5fa5a4be-c55a-4614-95e7-f92676eaeb32\",\"trace_id\":\"fcf37132-7a76-4dd4-8e90-ad50e74d299f\"}\n[2026-05-27 16:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5fa5a4be-c55a-4614-95e7-f92676eaeb32\",\"trace_id\":\"fcf37132-7a76-4dd4-8e90-ad50e74d299f\"}\n[2026-05-27 17:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4fcee8ab-7c17-4474-9070-83132139cec5\",\"trace_id\":\"29069de8-5869-4e80-8eea-609e70d99bee\"}\n[2026-05-27 17:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4fcee8ab-7c17-4474-9070-83132139cec5\",\"trace_id\":\"29069de8-5869-4e80-8eea-609e70d99bee\"}\n[2026-05-27 17:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"537b7577-77ba-4896-ad4a-dcedd1877d3b\",\"trace_id\":\"6d3ed1b0-6fcd-4128-a479-27658f328642\"}\n[2026-05-27 17:05:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"537b7577-77ba-4896-ad4a-dcedd1877d3b\",\"trace_id\":\"6d3ed1b0-6fcd-4128-a479-27658f328642\"}\n[2026-05-27 17:10:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0704fdc6-7b13-48fc-8471-d5b02041e7a0\",\"trace_id\":\"071d0b93-27b5-437b-bdce-747dc0b6e427\"}\n[2026-05-27 17:10:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0704fdc6-7b13-48fc-8471-d5b02041e7a0\",\"trace_id\":\"071d0b93-27b5-437b-bdce-747dc0b6e427\"}\n[2026-05-27 17:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"15ebdda0-0fa5-41c3-a1f3-495298d02859\",\"trace_id\":\"3ff4f628-3477-4362-8093-175b6655f290\"}\n[2026-05-27 17:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"15ebdda0-0fa5-41c3-a1f3-495298d02859\",\"trace_id\":\"3ff4f628-3477-4362-8093-175b6655f290\"}\n[2026-05-27 17:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f\",\"trace_id\":\"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6\"}\n[2026-05-27 17:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f\",\"trace_id\":\"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6\"}\n[2026-05-27 17:25:15] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2dab25b9-1a2d-417d-a88c-b086be423fa9\",\"trace_id\":\"e4339f70-a4e7-44ec-836b-a779aefbef3e\"}\n[2026-05-27 17:25:15] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2dab25b9-1a2d-417d-a88c-b086be423fa9\",\"trace_id\":\"e4339f70-a4e7-44ec-836b-a779aefbef3e\"}\n[2026-05-27 17:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a\",\"trace_id\":\"b80243e5-07bb-41e6-b3a1-26d27a4f13f0\"}\n[2026-05-27 17:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a\",\"trace_id\":\"b80243e5-07bb-41e6-b3a1-26d27a4f13f0\"}\n[2026-05-27 17:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dddd5b95-8803-4a70-af03-8a7174ca4a2b\",\"trace_id\":\"176bc409-1338-47d7-96bc-d6205480794d\"}\n[2026-05-27 17:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dddd5b95-8803-4a70-af03-8a7174ca4a2b\",\"trace_id\":\"176bc409-1338-47d7-96bc-d6205480794d\"}\n[2026-05-27 17:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4771c3be-e28d-4547-bc94-e2285ba51620\",\"trace_id\":\"3355584d-0daa-4a8a-b121-9482a0db6783\"}\n[2026-05-27 17:40:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4771c3be-e28d-4547-bc94-e2285ba51620\",\"trace_id\":\"3355584d-0daa-4a8a-b121-9482a0db6783\"}\n[2026-05-27 17:45:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"78d44e98-0241-4e33-b3d0-6adfb2a368fb\",\"trace_id\":\"465221db-a122-4ec4-9746-14e9fd0fa5fc\"}\n[2026-05-27 17:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"78d44e98-0241-4e33-b3d0-6adfb2a368fb\",\"trace_id\":\"465221db-a122-4ec4-9746-14e9fd0fa5fc\"}\n[2026-05-27 17:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"1d211a89-6b11-4803-a6b7-2e24f6710a52\",\"trace_id\":\"b24cc5f0-e0c7-4840-bd3a-0174203a2f72\"}\n[2026-05-27 17:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"1d211a89-6b11-4803-a6b7-2e24f6710a52\",\"trace_id\":\"b24cc5f0-e0c7-4840-bd3a-0174203a2f72\"}\n[2026-05-27 17:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"28c49b47-30c8-4d9a-83cd-b7d31b14bb37\",\"trace_id\":\"14d95738-e32a-4694-b2a8-8c327237940b\"}\n[2026-05-27 17:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"28c49b47-30c8-4d9a-83cd-b7d31b14bb37\",\"trace_id\":\"14d95738-e32a-4694-b2a8-8c327237940b\"}\n[2026-05-27 18:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"42220f4c-481c-46bd-ae41-326184f16003\",\"trace_id\":\"3f2ed406-60ed-4941-883b-cb8a456b1b10\"}\n[2026-05-27 18:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"42220f4c-481c-46bd-ae41-326184f16003\",\"trace_id\":\"3f2ed406-60ed-4941-883b-cb8a456b1b10\"}\n[2026-05-27 18:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"93db33bd-d0bd-46ea-8030-ae24b36ae963\",\"trace_id\":\"4696955a-81e1-4105-b8dd-61ff7a4f5584\"}\n[2026-05-27 18:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"93db33bd-d0bd-46ea-8030-ae24b36ae963\",\"trace_id\":\"4696955a-81e1-4105-b8dd-61ff7a4f5584\"}\n[2026-05-27 18:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b0ede837-0cc5-479a-ab75-db5c4101476f\",\"trace_id\":\"d7f0855f-051c-41e2-b67c-0afb6a0c294e\"}\n[2026-05-27 18:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b0ede837-0cc5-479a-ab75-db5c4101476f\",\"trace_id\":\"d7f0855f-051c-41e2-b67c-0afb6a0c294e\"}\n[2026-05-27 18:15:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b1d06246-dd9a-482e-bca8-658da1b25c37\",\"trace_id\":\"9146c9f3-e05f-489d-95f4-5f2c3b988440\"}\n[2026-05-27 18:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b1d06246-dd9a-482e-bca8-658da1b25c37\",\"trace_id\":\"9146c9f3-e05f-489d-95f4-5f2c3b988440\"}\n[2026-05-27 18:20:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e2a28a86-e3c3-46c3-88c5-209198f50961\",\"trace_id\":\"9e56205b-18a8-4a25-a181-183a9ffbf4d4\"}\n[2026-05-27 18:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e2a28a86-e3c3-46c3-88c5-209198f50961\",\"trace_id\":\"9e56205b-18a8-4a25-a181-183a9ffbf4d4\"}\n[2026-05-27 18:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4926c519-81cb-435e-a7db-e0cf00a90c14\",\"trace_id\":\"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c\"}\n[2026-05-27 18:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4926c519-81cb-435e-a7db-e0cf00a90c14\",\"trace_id\":\"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c\"}\n[2026-05-27 18:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"93963a2f-85d9-4cc5-a249-82262b8edaf8\",\"trace_id\":\"df8fcf0f-7260-45ec-ae3b-ed556bad8a69\"}\n[2026-05-27 18:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"93963a2f-85d9-4cc5-a249-82262b8edaf8\",\"trace_id\":\"df8fcf0f-7260-45ec-ae3b-ed556bad8a69\"}\n[2026-05-27 18:35:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e99c406d-511d-4593-8539-0264e7b2172b\",\"trace_id\":\"47f52e14-dd88-428e-86af-8b252ac156b6\"}\n[2026-05-27 18:35:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e99c406d-511d-4593-8539-0264e7b2172b\",\"trace_id\":\"47f52e14-dd88-428e-86af-8b252ac156b6\"}\n[2026-05-27 18:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e6d0ee61-1297-496e-84a2-8b81855e39b3\",\"trace_id\":\"90f74038-8fbd-4634-95d1-6b8f9eefd7b4\"}\n[2026-05-27 18:40:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e6d0ee61-1297-496e-84a2-8b81855e39b3\",\"trace_id\":\"90f74038-8fbd-4634-95d1-6b8f9eefd7b4\"}\n[2026-05-27 18:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"75258aa9-6788-4051-963b-914c871bbc26\",\"trace_id\":\"86b6f05b-9049-4e3c-a73e-6585c75750ae\"}\n[2026-05-27 18:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"75258aa9-6788-4051-963b-914c871bbc26\",\"trace_id\":\"86b6f05b-9049-4e3c-a73e-6585c75750ae\"}\n[2026-05-27 18:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"881c5f9a-d1aa-40a1-bda0-090fed822fea\",\"trace_id\":\"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924\"}\n[2026-05-27 18:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"881c5f9a-d1aa-40a1-bda0-090fed822fea\",\"trace_id\":\"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924\"}\n[2026-05-27 18:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"991ee58d-b151-4462-bda3-94ca8d8aae80\",\"trace_id\":\"6bbdfd15-e1f3-40da-8f33-3486b4fab01c\"}\n[2026-05-27 18:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"991ee58d-b151-4462-bda3-94ca8d8aae80\",\"trace_id\":\"6bbdfd15-e1f3-40da-8f33-3486b4fab01c\"}\n[2026-05-27 22:30:28] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b435dbfd-b566-49d2-b863-abb69460900c\",\"trace_id\":\"89070b16-0908-46c7-9804-46717c0893a2\"}\n[2026-05-27 22:30:29] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b435dbfd-b566-49d2-b863-abb69460900c\",\"trace_id\":\"89070b16-0908-46c7-9804-46717c0893a2\"}\n[2026-05-28 02:35:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"54253196-1163-4487-bfe0-d66ab8141278\",\"trace_id\":\"e624f869-6e34-4a09-9aa6-318387902ae0\"}\n[2026-05-28 02:35:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"54253196-1163-4487-bfe0-d66ab8141278\",\"trace_id\":\"e624f869-6e34-4a09-9aa6-318387902ae0\"}\n[2026-05-28 03:40:19] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a\",\"trace_id\":\"6718951a-b93d-4018-9306-1d2a9fc08947\"}\n[2026-05-28 03:40:20] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a\",\"trace_id\":\"6718951a-b93d-4018-9306-1d2a9fc08947\"}\n[2026-05-28 05:15:19] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"40a9b437-3914-4fc9-bc30-0928dc6ca2e4\",\"trace_id\":\"71233b87-f2d0-4aaa-9b89-4cf0bdab4416\"}\n[2026-05-28 05:15:19] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"40a9b437-3914-4fc9-bc30-0928dc6ca2e4\",\"trace_id\":\"71233b87-f2d0-4aaa-9b89-4cf0bdab4416\"}\n[2026-05-28 05:45:32] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"df61a176-ebde-4287-a6b3-4c279ecbef90\",\"trace_id\":\"16d667db-a08d-416f-a0b6-de6aa91d95e6\"}\n[2026-05-28 05:45:32] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"df61a176-ebde-4287-a6b3-4c279ecbef90\",\"trace_id\":\"16d667db-a08d-416f-a0b6-de6aa91d95e6\"}\n[2026-05-28 05:50:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dc4ad47f-f741-4320-89f3-69916d6c0e48\",\"trace_id\":\"ffcdcb94-a2d5-4277-add2-eb2449b5c82e\"}\n[2026-05-28 05:50:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dc4ad47f-f741-4320-89f3-69916d6c0e48\",\"trace_id\":\"ffcdcb94-a2d5-4277-add2-eb2449b5c82e\"}\n[2026-05-28 05:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4be209e1-8831-49f4-8d94-62852512812a\",\"trace_id\":\"a2e0926d-7628-43b2-9a24-68beddc58f0e\"}\n[2026-05-28 05:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4be209e1-8831-49f4-8d94-62852512812a\",\"trace_id\":\"a2e0926d-7628-43b2-9a24-68beddc58f0e\"}\n[2026-05-28 06:00:13] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"42647b91-48cd-4506-9527-60b7dd707516\",\"trace_id\":\"30f5a8fd-b99c-477f-b44c-6d13d2df15e4\"}\n[2026-05-28 06:00:16] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"42647b91-48cd-4506-9527-60b7dd707516\",\"trace_id\":\"30f5a8fd-b99c-477f-b44c-6d13d2df15e4\"}\n[2026-05-28 06:05:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e98fd078-62a9-4496-bd8f-9a5b209b7938\",\"trace_id\":\"f2c87487-7772-4543-b931-fdf7095a50b2\"}\n[2026-05-28 06:05:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e98fd078-62a9-4496-bd8f-9a5b209b7938\",\"trace_id\":\"f2c87487-7772-4543-b931-fdf7095a50b2\"}\n[2026-05-28 06:10:16] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"92743ccb-6275-4aef-948a-962d2116f793\",\"trace_id\":\"6ac60160-dc90-4b6c-8e90-2bcb842751d7\"}\n[2026-05-28 06:10:16] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"92743ccb-6275-4aef-948a-962d2116f793\",\"trace_id\":\"6ac60160-dc90-4b6c-8e90-2bcb842751d7\"}\n[2026-05-28 06:15:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e36a54ec-35c2-4440-8fd3-b88b4690fcb8\",\"trace_id\":\"d5e1a237-9248-4fde-a2a4-e24c61267db9\"}\n[2026-05-28 06:15:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e36a54ec-35c2-4440-8fd3-b88b4690fcb8\",\"trace_id\":\"d5e1a237-9248-4fde-a2a4-e24c61267db9\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5140412069093217780
|
-4525387198509650472
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
70
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:05:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:05:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:15:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:15:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:45:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:50:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 13:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 14:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:25:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:25:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:40:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 14:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 15:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:00:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:45:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:55:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 15:55:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 16:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:10:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:15:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
[2026-05-27 16:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
[2026-05-27 16:20:13] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2428f3a1-ffe7-47f6-9daa-3b37f277efe3","trace_id":"273bfee4-22a7-4ae9-85be-912f0f7d5ae5"}
[2026-05-27 16:20:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2428f3a1-ffe7-47f6-9daa-3b37f277efe3","trace_id":"273bfee4-22a7-4ae9-85be-912f0f7d5ae5"}
[2026-05-27 16:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0af368dd-50a6-4ae1-b384-a47434232748","trace_id":"a50d2d41-72b2-44b9-8c21-da2fe7cd2350"}
[2026-05-27 16:25:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0af368dd-50a6-4ae1-b384-a47434232748","trace_id":"a50d2d41-72b2-44b9-8c21-da2fe7cd2350"}
[2026-05-27 16:30:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7d75d5d3-7f4e-480b-beff-573be45c7d53","trace_id":"844a2d8a-624f-442a-9354-e4fcef280e0c"}
[2026-05-27 16:30:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7d75d5d3-7f4e-480b-beff-573be45c7d53","trace_id":"844a2d8a-624f-442a-9354-e4fcef280e0c"}
[2026-05-27 16:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"6d53364f-05f6-4d96-aac6-b1aab4359504","trace_id":"48efd308-178d-41ad-b1b2-1a09bc49ab01"}
[2026-05-27 16:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"6d53364f-05f6-4d96-aac6-b1aab4359504","trace_id":"48efd308-178d-41ad-b1b2-1a09bc49ab01"}
[2026-05-27 16:40:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"261d2acb-2d4e-47bd-a6cc-baefeb5675c4","trace_id":"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0"}
[2026-05-27 16:40:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"261d2acb-2d4e-47bd-a6cc-baefeb5675c4","trace_id":"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0"}
[2026-05-27 16:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b758c907-bbea-4bda-8f8d-028da9da8ce4","trace_id":"655a6ccc-e93f-4757-8abc-e6f6dd1f6755"}
[2026-05-27 16:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b758c907-bbea-4bda-8f8d-028da9da8ce4","trace_id":"655a6ccc-e93f-4757-8abc-e6f6dd1f6755"}
[2026-05-27 16:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7a56f511-63a9-44f4-8917-be2add000b25","trace_id":"70a3f353-bd37-479f-a3a4-090724cd4899"}
[2026-05-27 16:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7a56f511-63a9-44f4-8917-be2add000b25","trace_id":"70a3f353-bd37-479f-a3a4-090724cd4899"}
[2026-05-27 16:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5fa5a4be-c55a-4614-95e7-f92676eaeb32","trace_id":"fcf37132-7a76-4dd4-8e90-ad50e74d299f"}
[2026-05-27 16:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5fa5a4be-c55a-4614-95e7-f92676eaeb32","trace_id":"fcf37132-7a76-4dd4-8e90-ad50e74d299f"}
[2026-05-27 17:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4fcee8ab-7c17-4474-9070-83132139cec5","trace_id":"29069de8-5869-4e80-8eea-609e70d99bee"}
[2026-05-27 17:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4fcee8ab-7c17-4474-9070-83132139cec5","trace_id":"29069de8-5869-4e80-8eea-609e70d99bee"}
[2026-05-27 17:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"537b7577-77ba-4896-ad4a-dcedd1877d3b","trace_id":"6d3ed1b0-6fcd-4128-a479-27658f328642"}
[2026-05-27 17:05:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"537b7577-77ba-4896-ad4a-dcedd1877d3b","trace_id":"6d3ed1b0-6fcd-4128-a479-27658f328642"}
[2026-05-27 17:10:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0704fdc6-7b13-48fc-8471-d5b02041e7a0","trace_id":"071d0b93-27b5-437b-bdce-747dc0b6e427"}
[2026-05-27 17:10:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0704fdc6-7b13-48fc-8471-d5b02041e7a0","trace_id":"071d0b93-27b5-437b-bdce-747dc0b6e427"}
[2026-05-27 17:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"15ebdda0-0fa5-41c3-a1f3-495298d02859","trace_id":"3ff4f628-3477-4362-8093-175b6655f290"}
[2026-05-27 17:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"15ebdda0-0fa5-41c3-a1f3-495298d02859","trace_id":"3ff4f628-3477-4362-8093-175b6655f290"}
[2026-05-27 17:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f","trace_id":"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6"}
[2026-05-27 17:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f","trace_id":"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6"}
[2026-05-27 17:25:15] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2dab25b9-1a2d-417d-a88c-b086be423fa9","trace_id":"e4339f70-a4e7-44ec-836b-a779aefbef3e"}
[2026-05-27 17:25:15] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2dab25b9-1a2d-417d-a88c-b086be423fa9","trace_id":"e4339f70-a4e7-44ec-836b-a779aefbef3e"}
[2026-05-27 17:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a","trace_id":"b80243e5-07bb-41e6-b3a1-26d27a4f13f0"}
[2026-05-27 17:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a","trace_id":"b80243e5-07bb-41e6-b3a1-26d27a4f13f0"}
[2026-05-27 17:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dddd5b95-8803-4a70-af03-8a7174ca4a2b","trace_id":"176bc409-1338-47d7-96bc-d6205480794d"}
[2026-05-27 17:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dddd5b95-8803-4a70-af03-8a7174ca4a2b","trace_id":"176bc409-1338-47d7-96bc-d6205480794d"}
[2026-05-27 17:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4771c3be-e28d-4547-bc94-e2285ba51620","trace_id":"3355584d-0daa-4a8a-b121-9482a0db6783"}
[2026-05-27 17:40:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4771c3be-e28d-4547-bc94-e2285ba51620","trace_id":"3355584d-0daa-4a8a-b121-9482a0db6783"}
[2026-05-27 17:45:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"78d44e98-0241-4e33-b3d0-6adfb2a368fb","trace_id":"465221db-a122-4ec4-9746-14e9fd0fa5fc"}
[2026-05-27 17:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"78d44e98-0241-4e33-b3d0-6adfb2a368fb","trace_id":"465221db-a122-4ec4-9746-14e9fd0fa5fc"}
[2026-05-27 17:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"1d211a89-6b11-4803-a6b7-2e24f6710a52","trace_id":"b24cc5f0-e0c7-4840-bd3a-0174203a2f72"}
[2026-05-27 17:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"1d211a89-6b11-4803-a6b7-2e24f6710a52","trace_id":"b24cc5f0-e0c7-4840-bd3a-0174203a2f72"}
[2026-05-27 17:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"28c49b47-30c8-4d9a-83cd-b7d31b14bb37","trace_id":"14d95738-e32a-4694-b2a8-8c327237940b"}
[2026-05-27 17:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"28c49b47-30c8-4d9a-83cd-b7d31b14bb37","trace_id":"14d95738-e32a-4694-b2a8-8c327237940b"}
[2026-05-27 18:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"42220f4c-481c-46bd-ae41-326184f16003","trace_id":"3f2ed406-60ed-4941-883b-cb8a456b1b10"}
[2026-05-27 18:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"42220f4c-481c-46bd-ae41-326184f16003","trace_id":"3f2ed406-60ed-4941-883b-cb8a456b1b10"}
[2026-05-27 18:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"93db33bd-d0bd-46ea-8030-ae24b36ae963","trace_id":"4696955a-81e1-4105-b8dd-61ff7a4f5584"}
[2026-05-27 18:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"93db33bd-d0bd-46ea-8030-ae24b36ae963","trace_id":"4696955a-81e1-4105-b8dd-61ff7a4f5584"}
[2026-05-27 18:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b0ede837-0cc5-479a-ab75-db5c4101476f","trace_id":"d7f0855f-051c-41e2-b67c-0afb6a0c294e"}
[2026-05-27 18:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b0ede837-0cc5-479a-ab75-db5c4101476f","trace_id":"d7f0855f-051c-41e2-b67c-0afb6a0c294e"}
[2026-05-27 18:15:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b1d06246-dd9a-482e-bca8-658da1b25c37","trace_id":"9146c9f3-e05f-489d-95f4-5f2c3b988440"}
[2026-05-27 18:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b1d06246-dd9a-482e-bca8-658da1b25c37","trace_id":"9146c9f3-e05f-489d-95f4-5f2c3b988440"}
[2026-05-27 18:20:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e2a28a86-e3c3-46c3-88c5-209198f50961","trace_id":"9e56205b-18a8-4a25-a181-183a9ffbf4d4"}
[2026-05-27 18:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e2a28a86-e3c3-46c3-88c5-209198f50961","trace_id":"9e56205b-18a8-4a25-a181-183a9ffbf4d4"}
[2026-05-27 18:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4926c519-81cb-435e-a7db-e0cf00a90c14","trace_id":"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c"}
[2026-05-27 18:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4926c519-81cb-435e-a7db-e0cf00a90c14","trace_id":"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c"}
[2026-05-27 18:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"93963a2f-85d9-4cc5-a249-82262b8edaf8","trace_id":"df8fcf0f-7260-45ec-ae3b-ed556bad8a69"}
[2026-05-27 18:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"93963a2f-85d9-4cc5-a249-82262b8edaf8","trace_id":"df8fcf0f-7260-45ec-ae3b-ed556bad8a69"}
[2026-05-27 18:35:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e99c406d-511d-4593-8539-0264e7b2172b","trace_id":"47f52e14-dd88-428e-86af-8b252ac156b6"}
[2026-05-27 18:35:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e99c406d-511d-4593-8539-0264e7b2172b","trace_id":"47f52e14-dd88-428e-86af-8b252ac156b6"}
[2026-05-27 18:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e6d0ee61-1297-496e-84a2-8b81855e39b3","trace_id":"90f74038-8fbd-4634-95d1-6b8f9eefd7b4"}
[2026-05-27 18:40:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e6d0ee61-1297-496e-84a2-8b81855e39b3","trace_id":"90f74038-8fbd-4634-95d1-6b8f9eefd7b4"}
[2026-05-27 18:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"75258aa9-6788-4051-963b-914c871bbc26","trace_id":"86b6f05b-9049-4e3c-a73e-6585c75750ae"}
[2026-05-27 18:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"75258aa9-6788-4051-963b-914c871bbc26","trace_id":"86b6f05b-9049-4e3c-a73e-6585c75750ae"}
[2026-05-27 18:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"881c5f9a-d1aa-40a1-bda0-090fed822fea","trace_id":"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924"}
[2026-05-27 18:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"881c5f9a-d1aa-40a1-bda0-090fed822fea","trace_id":"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924"}
[2026-05-27 18:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"991ee58d-b151-4462-bda3-94ca8d8aae80","trace_id":"6bbdfd15-e1f3-40da-8f33-3486b4fab01c"}
[2026-05-27 18:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"991ee58d-b151-4462-bda3-94ca8d8aae80","trace_id":"6bbdfd15-e1f3-40da-8f33-3486b4fab01c"}
[2026-05-27 22:30:28] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b435dbfd-b566-49d2-b863-abb69460900c","trace_id":"89070b16-0908-46c7-9804-46717c0893a2"}
[2026-05-27 22:30:29] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b435dbfd-b566-49d2-b863-abb69460900c","trace_id":"89070b16-0908-46c7-9804-46717c0893a2"}
[2026-05-28 02:35:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"[CREDIT_CARD]-bfe0-d66ab8141278","trace_id":"e624f869-6e34-4a09-9aa6-318387902ae0"}
[2026-05-28 02:35:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"[CREDIT_CARD]-bfe0-d66ab8141278","trace_id":"e624f869-6e34-4a09-9aa6-318387902ae0"}
[2026-05-28 03:40:19] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a","trace_id":"6718951a-b93d-4018-9306-1d2a9fc08947"}
[2026-05-28 03:40:20] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a","trace_id":"6718951a-b93d-4018-9306-1d2a9fc08947"}
[2026-05-28 05:15:19] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"40a9b437-3914-4fc9-bc30-0928dc6ca2e4","trace_id":"71233b87-f2d0-4aaa-9b89-4cf0bdab4416"}
[2026-05-28 05:15:19] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"40a9b437-3914-4fc9-bc30-0928dc6ca2e4","trace_id":"71233b87-f2d0-4aaa-9b89-4cf0bdab4416"}
[2026-05-28 05:45:32] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"df61a176-ebde-4287-a6b3-4c279ecbef90","trace_id":"16d667db-a08d-416f-a0b6-de6aa91d95e6"}
[2026-05-28 05:45:32] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"df61a176-ebde-4287-a6b3-4c279ecbef90","trace_id":"16d667db-a08d-416f-a0b6-de6aa91d95e6"}
[2026-05-28 05:50:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dc4ad47f-f741-4320-89f3-69916d6c0e48","trace_id":"ffcdcb94-a2d5-4277-add2-eb2449b5c82e"}
[2026-05-28 05:50:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dc4ad47f-f741-4320-89f3-69916d6c0e48","trace_id":"ffcdcb94-a2d5-4277-add2-eb2449b5c82e"}
[2026-05-28 05:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4be209e1-8831-49f4-8d94-62852512812a","trace_id":"a2e0926d-7628-43b2-9a24-68beddc58f0e"}
[2026-05-28 05:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4be209e1-8831-49f4-8d94-62852512812a","trace_id":"a2e0926d-7628-43b2-9a24-68beddc58f0e"}
[2026-05-28 06:00:13] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"42647b91-48cd-4506-9527-60b7dd707516","trace_id":"30f5a8fd-b99c-477f-b44c-6d13d2df15e4"}
[2026-05-28 06:00:16] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"42647b91-48cd-4506-9527-60b7dd707516","trace_id":"30f5a8fd-b99c-477f-b44c-6d13d2df15e4"}
[2026-05-28 06:05:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e98fd078-62a9-4496-bd8f-9a5b209b7938","trace_id":"f2c87487-7772-4543-b931-fdf7095a50b2"}
[2026-05-28 06:05:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e98fd078-62a9-4496-bd8f-9a5b209b7938","trace_id":"f2c87487-7772-4543-b931-fdf7095a50b2"}
[2026-05-28 06:10:16] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"92743ccb-6275-4aef-948a-962d2116f793","trace_id":"6ac60160-dc90-4b6c-8e90-2bcb842751d7"}
[2026-05-28 06:10:16] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"92743ccb-6275-4aef-948a-962d2116f793","trace_id":"6ac60160-dc90-4b6c-8e90-2bcb842751d7"}
[2026-05-28 06:15:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e36a54ec-35c2-4440-8fd3-b88b4690fcb8","trace_id":"d5e1a237-9248-4fde-a2a4-e24c61267db9"}
[2026-05-28 06:15:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e36a54ec-35c2-4440-8fd3-b88b4690fcb8","trace_id":"d5e1a237-9248-4fde-a2a4-e24c61267db9"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
79076
|
NULL
|
NULL
|
NULL
|
|
79078
|
2780
|
12
|
2026-05-28T06:17:29.621191+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779949049621_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
70
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:05:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:05:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:15:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:15:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:45:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:50:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 13:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 14:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:25:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:25:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:40:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 14:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 15:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:00:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:45:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:55:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 15:55:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 16:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:10:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:15:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
[2026-05-27 16:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
[2026-05-27 16:20:13] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2428f3a1-ffe7-47f6-9daa-3b37f277efe3","trace_id":"273bfee4-22a7-4ae9-85be-912f0f7d5ae5"}
[2026-05-27 16:20:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2428f3a1-ffe7-47f6-9daa-3b37f277efe3","trace_id":"273bfee4-22a7-4ae9-85be-912f0f7d5ae5"}
[2026-05-27 16:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0af368dd-50a6-4ae1-b384-a47434232748","trace_id":"a50d2d41-72b2-44b9-8c21-da2fe7cd2350"}
[2026-05-27 16:25:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0af368dd-50a6-4ae1-b384-a47434232748","trace_id":"a50d2d41-72b2-44b9-8c21-da2fe7cd2350"}
[2026-05-27 16:30:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7d75d5d3-7f4e-480b-beff-573be45c7d53","trace_id":"844a2d8a-624f-442a-9354-e4fcef280e0c"}
[2026-05-27 16:30:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7d75d5d3-7f4e-480b-beff-573be45c7d53","trace_id":"844a2d8a-624f-442a-9354-e4fcef280e0c"}
[2026-05-27 16:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"6d53364f-05f6-4d96-aac6-b1aab4359504","trace_id":"48efd308-178d-41ad-b1b2-1a09bc49ab01"}
[2026-05-27 16:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"6d53364f-05f6-4d96-aac6-b1aab4359504","trace_id":"48efd308-178d-41ad-b1b2-1a09bc49ab01"}
[2026-05-27 16:40:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"261d2acb-2d4e-47bd-a6cc-baefeb5675c4","trace_id":"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0"}
[2026-05-27 16:40:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"261d2acb-2d4e-47bd-a6cc-baefeb5675c4","trace_id":"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0"}
[2026-05-27 16:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b758c907-bbea-4bda-8f8d-028da9da8ce4","trace_id":"655a6ccc-e93f-4757-8abc-e6f6dd1f6755"}
[2026-05-27 16:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b758c907-bbea-4bda-8f8d-028da9da8ce4","trace_id":"655a6ccc-e93f-4757-8abc-e6f6dd1f6755"}
[2026-05-27 16:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7a56f511-63a9-44f4-8917-be2add000b25","trace_id":"70a3f353-bd37-479f-a3a4-090724cd4899"}
[2026-05-27 16:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7a56f511-63a9-44f4-8917-be2add000b25","trace_id":"70a3f353-bd37-479f-a3a4-090724cd4899"}
[2026-05-27 16:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5fa5a4be-c55a-4614-95e7-f92676eaeb32","trace_id":"fcf37132-7a76-4dd4-8e90-ad50e74d299f"}
[2026-05-27 16:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5fa5a4be-c55a-4614-95e7-f92676eaeb32","trace_id":"fcf37132-7a76-4dd4-8e90-ad50e74d299f"}
[2026-05-27 17:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4fcee8ab-7c17-4474-9070-83132139cec5","trace_id":"29069de8-5869-4e80-8eea-609e70d99bee"}
[2026-05-27 17:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4fcee8ab-7c17-4474-9070-83132139cec5","trace_id":"29069de8-5869-4e80-8eea-609e70d99bee"}
[2026-05-27 17:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"537b7577-77ba-4896-ad4a-dcedd1877d3b","trace_id":"6d3ed1b0-6fcd-4128-a479-27658f328642"}
[2026-05-27 17:05:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"537b7577-77ba-4896-ad4a-dcedd1877d3b","trace_id":"6d3ed1b0-6fcd-4128-a479-27658f328642"}
[2026-05-27 17:10:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0704fdc6-7b13-48fc-8471-d5b02041e7a0","trace_id":"071d0b93-27b5-437b-bdce-747dc0b6e427"}
[2026-05-27 17:10:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0704fdc6-7b13-48fc-8471-d5b02041e7a0","trace_id":"071d0b93-27b5-437b-bdce-747dc0b6e427"}
[2026-05-27 17:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"15ebdda0-0fa5-41c3-a1f3-495298d02859","trace_id":"3ff4f628-3477-4362-8093-175b6655f290"}
[2026-05-27 17:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"15ebdda0-0fa5-41c3-a1f3-495298d02859","trace_id":"3ff4f628-3477-4362-8093-175b6655f290"}
[2026-05-27 17:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f","trace_id":"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6"}
[2026-05-27 17:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f","trace_id":"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6"}
[2026-05-27 17:25:15] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2dab25b9-1a2d-417d-a88c-b086be423fa9","trace_id":"e4339f70-a4e7-44ec-836b-a779aefbef3e"}
[2026-05-27 17:25:15] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2dab25b9-1a2d-417d-a88c-b086be423fa9","trace_id":"e4339f70-a4e7-44ec-836b-a779aefbef3e"}
[2026-05-27 17:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a","trace_id":"b80243e5-07bb-41e6-b3a1-26d27a4f13f0"}
[2026-05-27 17:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a","trace_id":"b80243e5-07bb-41e6-b3a1-26d27a4f13f0"}
[2026-05-27 17:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dddd5b95-8803-4a70-af03-8a7174ca4a2b","trace_id":"176bc409-1338-47d7-96bc-d6205480794d"}
[2026-05-27 17:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dddd5b95-8803-4a70-af03-8a7174ca4a2b","trace_id":"176bc409-1338-47d7-96bc-d6205480794d"}
[2026-05-27 17:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4771c3be-e28d-4547-bc94-e2285ba51620","trace_id":"3355584d-0daa-4a8a-b121-9482a0db6783"}
[2026-05-27 17:40:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4771c3be-e28d-4547-bc94-e2285ba51620","trace_id":"3355584d-0daa-4a8a-b121-9482a0db6783"}
[2026-05-27 17:45:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"78d44e98-0241-4e33-b3d0-6adfb2a368fb","trace_id":"465221db-a122-4ec4-9746-14e9fd0fa5fc"}
[2026-05-27 17:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"78d44e98-0241-4e33-b3d0-6adfb2a368fb","trace_id":"465221db-a122-4ec4-9746-14e9fd0fa5fc"}
[2026-05-27 17:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"1d211a89-6b11-4803-a6b7-2e24f6710a52","trace_id":"b24cc5f0-e0c7-4840-bd3a-0174203a2f72"}
[2026-05-27 17:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"1d211a89-6b11-4803-a6b7-2e24f6710a52","trace_id":"b24cc5f0-e0c7-4840-bd3a-0174203a2f72"}
[2026-05-27 17:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"28c49b47-30c8-4d9a-83cd-b7d31b14bb37","trace_id":"14d95738-e32a-4694-b2a8-8c327237940b"}
[2026-05-27 17:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"28c49b47-30c8-4d9a-83cd-b7d31b14bb37","trace_id":"14d95738-e32a-4694-b2a8-8c327237940b"}
[2026-05-27 18:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"42220f4c-481c-46bd-ae41-326184f16003","trace_id":"3f2ed406-60ed-4941-883b-cb8a456b1b10"}
[2026-05-27 18:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"42220f4c-481c-46bd-ae41-326184f16003","trace_id":"3f2ed406-60ed-4941-883b-cb8a456b1b10"}
[2026-05-27 18:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"93db33bd-d0bd-46ea-8030-ae24b36ae963","trace_id":"4696955a-81e1-4105-b8dd-61ff7a4f5584"}
[2026-05-27 18:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"93db33bd-d0bd-46ea-8030-ae24b36ae963","trace_id":"4696955a-81e1-4105-b8dd-61ff7a4f5584"}
[2026-05-27 18:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b0ede837-0cc5-479a-ab75-db5c4101476f","trace_id":"d7f0855f-051c-41e2-b67c-0afb6a0c294e"}
[2026-05-27 18:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b0ede837-0cc5-479a-ab75-db5c4101476f","trace_id":"d7f0855f-051c-41e2-b67c-0afb6a0c294e"}
[2026-05-27 18:15:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b1d06246-dd9a-482e-bca8-658da1b25c37","trace_id":"9146c9f3-e05f-489d-95f4-5f2c3b988440"}
[2026-05-27 18:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b1d06246-dd9a-482e-bca8-658da1b25c37","trace_id":"9146c9f3-e05f-489d-95f4-5f2c3b988440"}
[2026-05-27 18:20:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e2a28a86-e3c3-46c3-88c5-209198f50961","trace_id":"9e56205b-18a8-4a25-a181-183a9ffbf4d4"}
[2026-05-27 18:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e2a28a86-e3c3-46c3-88c5-209198f50961","trace_id":"9e56205b-18a8-4a25-a181-183a9ffbf4d4"}
[2026-05-27 18:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4926c519-81cb-435e-a7db-e0cf00a90c14","trace_id":"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c"}
[2026-05-27 18:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4926c519-81cb-435e-a7db-e0cf00a90c14","trace_id":"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c"}
[2026-05-27 18:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"93963a2f-85d9-4cc5-a249-82262b8edaf8","trace_id":"df8fcf0f-7260-45ec-ae3b-ed556bad8a69"}
[2026-05-27 18:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"93963a2f-85d9-4cc5-a249-82262b8edaf8","trace_id":"df8fcf0f-7260-45ec-ae3b-ed556bad8a69"}
[2026-05-27 18:35:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e99c406d-511d-4593-8539-0264e7b2172b","trace_id":"47f52e14-dd88-428e-86af-8b252ac156b6"}
[2026-05-27 18:35:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e99c406d-511d-4593-8539-0264e7b2172b","trace_id":"47f52e14-dd88-428e-86af-8b252ac156b6"}
[2026-05-27 18:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e6d0ee61-1297-496e-84a2-8b81855e39b3","trace_id":"90f74038-8fbd-4634-95d1-6b8f9eefd7b4"}
[2026-05-27 18:40:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e6d0ee61-1297-496e-84a2-8b81855e39b3","trace_id":"90f74038-8fbd-4634-95d1-6b8f9eefd7b4"}
[2026-05-27 18:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"75258aa9-6788-4051-963b-914c871bbc26","trace_id":"86b6f05b-9049-4e3c-a73e-6585c75750ae"}
[2026-05-27 18:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"75258aa9-6788-4051-963b-914c871bbc26","trace_id":"86b6f05b-9049-4e3c-a73e-6585c75750ae"}
[2026-05-27 18:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"881c5f9a-d1aa-40a1-bda0-090fed822fea","trace_id":"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924"}
[2026-05-27 18:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"881c5f9a-d1aa-40a1-bda0-090fed822fea","trace_id":"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924"}
[2026-05-27 18:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"991ee58d-b151-4462-bda3-94ca8d8aae80","trace_id":"6bbdfd15-e1f3-40da-8f33-3486b4fab01c"}
[2026-05-27 18:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"991ee58d-b151-4462-bda3-94ca8d8aae80","trace_id":"6bbdfd15-e1f3-40da-8f33-3486b4fab01c"}
[2026-05-27 22:30:28] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b435dbfd-b566-49d2-b863-abb69460900c","trace_id":"89070b16-0908-46c7-9804-46717c0893a2"}
[2026-05-27 22:30:29] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b435dbfd-b566-49d2-b863-abb69460900c","trace_id":"89070b16-0908-46c7-9804-46717c0893a2"}
[2026-05-28 02:35:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"[CREDIT_CARD]-bfe0-d66ab8141278","trace_id":"e624f869-6e34-4a09-9aa6-318387902ae0"}
[2026-05-28 02:35:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"[CREDIT_CARD]-bfe0-d66ab8141278","trace_id":"e624f869-6e34-4a09-9aa6-318387902ae0"}
[2026-05-28 03:40:19] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a","trace_id":"6718951a-b93d-4018-9306-1d2a9fc08947"}
[2026-05-28 03:40:20] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a","trace_id":"6718951a-b93d-4018-9306-1d2a9fc08947"}
[2026-05-28 05:15:19] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"40a9b437-3914-4fc9-bc30-0928dc6ca2e4","trace_id":"71233b87-f2d0-4aaa-9b89-4cf0bdab4416"}
[2026-05-28 05:15:19] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"40a9b437-3914-4fc9-bc30-0928dc6ca2e4","trace_id":"71233b87-f2d0-4aaa-9b89-4cf0bdab4416"}
[2026-05-28 05:45:32] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"df61a176-ebde-4287-a6b3-4c279ecbef90","trace_id":"16d667db-a08d-416f-a0b6-de6aa91d95e6"}
[2026-05-28 05:45:32] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"df61a176-ebde-4287-a6b3-4c279ecbef90","trace_id":"16d667db-a08d-416f-a0b6-de6aa91d95e6"}
[2026-05-28 05:50:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dc4ad47f-f741-4320-89f3-69916d6c0e48","trace_id":"ffcdcb94-a2d5-4277-add2-eb2449b5c82e"}
[2026-05-28 05:50:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dc4ad47f-f741-4320-89f3-69916d6c0e48","trace_id":"ffcdcb94-a2d5-4277-add2-eb2449b5c82e"}
[2026-05-28 05:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4be209e1-8831-49f4-8d94-62852512812a","trace_id":"a2e0926d-7628-43b2-9a24-68beddc58f0e"}
[2026-05-28 05:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4be209e1-8831-49f4-8d94-62852512812a","trace_id":"a2e0926d-7628-43b2-9a24-68beddc58f0e"}
[2026-05-28 06:00:13] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"42647b91-48cd-4506-9527-60b7dd707516","trace_id":"30f5a8fd-b99c-477f-b44c-6d13d2df15e4"}
[2026-05-28 06:00:16] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"42647b91-48cd-4506-9527-60b7dd707516","trace_id":"30f5a8fd-b99c-477f-b44c-6d13d2df15e4"}
[2026-05-28 06:05:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e98fd078-62a9-4496-bd8f-9a5b209b7938","trace_id":"f2c87487-7772-4543-b931-fdf7095a50b2"}
[2026-05-28 06:05:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e98fd078-62a9-4496-bd8f-9a5b209b7938","trace_id":"f2c87487-7772-4543-b931-fdf7095a50b2"}
[2026-05-28 06:10:16] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"92743ccb-6275-4aef-948a-962d2116f793","trace_id":"6ac60160-dc90-4b6c-8e90-2bcb842751d7"}
[2026-05-28 06:10:16] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"92743ccb-6275-4aef-948a-962d2116f793","trace_id":"6ac60160-dc90-4b6c-8e90-2bcb842751d7"}
[2026-05-28 06:15:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e36a54ec-35c2-4440-8fd3-b88b4690fcb8","trace_id":"d5e1a237-9248-4fde-a2a4-e24c61267db9"}
[2026-05-28 06:15:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e36a54ec-35c2-4440-8fd3-b88b4690fcb8","trace_id":"d5e1a237-9248-4fde-a2a4-e24c61267db9"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"70","depth":4,"bounds":{"left":0.7273936,"top":0.10055866,"width":0.009973404,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.7390292,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.7463431,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-27 13:05:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:05:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:15:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:15:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:45:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:50:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 13:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 14:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:25:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:25:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:40:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 14:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 15:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:00:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:45:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:55:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 15:55:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 16:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:10:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:15:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}\n[2026-05-27 16:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}\n[2026-05-27 16:20:13] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2428f3a1-ffe7-47f6-9daa-3b37f277efe3\",\"trace_id\":\"273bfee4-22a7-4ae9-85be-912f0f7d5ae5\"}\n[2026-05-27 16:20:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2428f3a1-ffe7-47f6-9daa-3b37f277efe3\",\"trace_id\":\"273bfee4-22a7-4ae9-85be-912f0f7d5ae5\"}\n[2026-05-27 16:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0af368dd-50a6-4ae1-b384-a47434232748\",\"trace_id\":\"a50d2d41-72b2-44b9-8c21-da2fe7cd2350\"}\n[2026-05-27 16:25:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0af368dd-50a6-4ae1-b384-a47434232748\",\"trace_id\":\"a50d2d41-72b2-44b9-8c21-da2fe7cd2350\"}\n[2026-05-27 16:30:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7d75d5d3-7f4e-480b-beff-573be45c7d53\",\"trace_id\":\"844a2d8a-624f-442a-9354-e4fcef280e0c\"}\n[2026-05-27 16:30:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7d75d5d3-7f4e-480b-beff-573be45c7d53\",\"trace_id\":\"844a2d8a-624f-442a-9354-e4fcef280e0c\"}\n[2026-05-27 16:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"6d53364f-05f6-4d96-aac6-b1aab4359504\",\"trace_id\":\"48efd308-178d-41ad-b1b2-1a09bc49ab01\"}\n[2026-05-27 16:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"6d53364f-05f6-4d96-aac6-b1aab4359504\",\"trace_id\":\"48efd308-178d-41ad-b1b2-1a09bc49ab01\"}\n[2026-05-27 16:40:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"261d2acb-2d4e-47bd-a6cc-baefeb5675c4\",\"trace_id\":\"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0\"}\n[2026-05-27 16:40:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"261d2acb-2d4e-47bd-a6cc-baefeb5675c4\",\"trace_id\":\"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0\"}\n[2026-05-27 16:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b758c907-bbea-4bda-8f8d-028da9da8ce4\",\"trace_id\":\"655a6ccc-e93f-4757-8abc-e6f6dd1f6755\"}\n[2026-05-27 16:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b758c907-bbea-4bda-8f8d-028da9da8ce4\",\"trace_id\":\"655a6ccc-e93f-4757-8abc-e6f6dd1f6755\"}\n[2026-05-27 16:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7a56f511-63a9-44f4-8917-be2add000b25\",\"trace_id\":\"70a3f353-bd37-479f-a3a4-090724cd4899\"}\n[2026-05-27 16:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7a56f511-63a9-44f4-8917-be2add000b25\",\"trace_id\":\"70a3f353-bd37-479f-a3a4-090724cd4899\"}\n[2026-05-27 16:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5fa5a4be-c55a-4614-95e7-f92676eaeb32\",\"trace_id\":\"fcf37132-7a76-4dd4-8e90-ad50e74d299f\"}\n[2026-05-27 16:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5fa5a4be-c55a-4614-95e7-f92676eaeb32\",\"trace_id\":\"fcf37132-7a76-4dd4-8e90-ad50e74d299f\"}\n[2026-05-27 17:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4fcee8ab-7c17-4474-9070-83132139cec5\",\"trace_id\":\"29069de8-5869-4e80-8eea-609e70d99bee\"}\n[2026-05-27 17:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4fcee8ab-7c17-4474-9070-83132139cec5\",\"trace_id\":\"29069de8-5869-4e80-8eea-609e70d99bee\"}\n[2026-05-27 17:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"537b7577-77ba-4896-ad4a-dcedd1877d3b\",\"trace_id\":\"6d3ed1b0-6fcd-4128-a479-27658f328642\"}\n[2026-05-27 17:05:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"537b7577-77ba-4896-ad4a-dcedd1877d3b\",\"trace_id\":\"6d3ed1b0-6fcd-4128-a479-27658f328642\"}\n[2026-05-27 17:10:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0704fdc6-7b13-48fc-8471-d5b02041e7a0\",\"trace_id\":\"071d0b93-27b5-437b-bdce-747dc0b6e427\"}\n[2026-05-27 17:10:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0704fdc6-7b13-48fc-8471-d5b02041e7a0\",\"trace_id\":\"071d0b93-27b5-437b-bdce-747dc0b6e427\"}\n[2026-05-27 17:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"15ebdda0-0fa5-41c3-a1f3-495298d02859\",\"trace_id\":\"3ff4f628-3477-4362-8093-175b6655f290\"}\n[2026-05-27 17:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"15ebdda0-0fa5-41c3-a1f3-495298d02859\",\"trace_id\":\"3ff4f628-3477-4362-8093-175b6655f290\"}\n[2026-05-27 17:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f\",\"trace_id\":\"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6\"}\n[2026-05-27 17:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f\",\"trace_id\":\"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6\"}\n[2026-05-27 17:25:15] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2dab25b9-1a2d-417d-a88c-b086be423fa9\",\"trace_id\":\"e4339f70-a4e7-44ec-836b-a779aefbef3e\"}\n[2026-05-27 17:25:15] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2dab25b9-1a2d-417d-a88c-b086be423fa9\",\"trace_id\":\"e4339f70-a4e7-44ec-836b-a779aefbef3e\"}\n[2026-05-27 17:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a\",\"trace_id\":\"b80243e5-07bb-41e6-b3a1-26d27a4f13f0\"}\n[2026-05-27 17:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a\",\"trace_id\":\"b80243e5-07bb-41e6-b3a1-26d27a4f13f0\"}\n[2026-05-27 17:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dddd5b95-8803-4a70-af03-8a7174ca4a2b\",\"trace_id\":\"176bc409-1338-47d7-96bc-d6205480794d\"}\n[2026-05-27 17:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dddd5b95-8803-4a70-af03-8a7174ca4a2b\",\"trace_id\":\"176bc409-1338-47d7-96bc-d6205480794d\"}\n[2026-05-27 17:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4771c3be-e28d-4547-bc94-e2285ba51620\",\"trace_id\":\"3355584d-0daa-4a8a-b121-9482a0db6783\"}\n[2026-05-27 17:40:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4771c3be-e28d-4547-bc94-e2285ba51620\",\"trace_id\":\"3355584d-0daa-4a8a-b121-9482a0db6783\"}\n[2026-05-27 17:45:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"78d44e98-0241-4e33-b3d0-6adfb2a368fb\",\"trace_id\":\"465221db-a122-4ec4-9746-14e9fd0fa5fc\"}\n[2026-05-27 17:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"78d44e98-0241-4e33-b3d0-6adfb2a368fb\",\"trace_id\":\"465221db-a122-4ec4-9746-14e9fd0fa5fc\"}\n[2026-05-27 17:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"1d211a89-6b11-4803-a6b7-2e24f6710a52\",\"trace_id\":\"b24cc5f0-e0c7-4840-bd3a-0174203a2f72\"}\n[2026-05-27 17:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"1d211a89-6b11-4803-a6b7-2e24f6710a52\",\"trace_id\":\"b24cc5f0-e0c7-4840-bd3a-0174203a2f72\"}\n[2026-05-27 17:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"28c49b47-30c8-4d9a-83cd-b7d31b14bb37\",\"trace_id\":\"14d95738-e32a-4694-b2a8-8c327237940b\"}\n[2026-05-27 17:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"28c49b47-30c8-4d9a-83cd-b7d31b14bb37\",\"trace_id\":\"14d95738-e32a-4694-b2a8-8c327237940b\"}\n[2026-05-27 18:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"42220f4c-481c-46bd-ae41-326184f16003\",\"trace_id\":\"3f2ed406-60ed-4941-883b-cb8a456b1b10\"}\n[2026-05-27 18:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"42220f4c-481c-46bd-ae41-326184f16003\",\"trace_id\":\"3f2ed406-60ed-4941-883b-cb8a456b1b10\"}\n[2026-05-27 18:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"93db33bd-d0bd-46ea-8030-ae24b36ae963\",\"trace_id\":\"4696955a-81e1-4105-b8dd-61ff7a4f5584\"}\n[2026-05-27 18:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"93db33bd-d0bd-46ea-8030-ae24b36ae963\",\"trace_id\":\"4696955a-81e1-4105-b8dd-61ff7a4f5584\"}\n[2026-05-27 18:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b0ede837-0cc5-479a-ab75-db5c4101476f\",\"trace_id\":\"d7f0855f-051c-41e2-b67c-0afb6a0c294e\"}\n[2026-05-27 18:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b0ede837-0cc5-479a-ab75-db5c4101476f\",\"trace_id\":\"d7f0855f-051c-41e2-b67c-0afb6a0c294e\"}\n[2026-05-27 18:15:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b1d06246-dd9a-482e-bca8-658da1b25c37\",\"trace_id\":\"9146c9f3-e05f-489d-95f4-5f2c3b988440\"}\n[2026-05-27 18:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b1d06246-dd9a-482e-bca8-658da1b25c37\",\"trace_id\":\"9146c9f3-e05f-489d-95f4-5f2c3b988440\"}\n[2026-05-27 18:20:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e2a28a86-e3c3-46c3-88c5-209198f50961\",\"trace_id\":\"9e56205b-18a8-4a25-a181-183a9ffbf4d4\"}\n[2026-05-27 18:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e2a28a86-e3c3-46c3-88c5-209198f50961\",\"trace_id\":\"9e56205b-18a8-4a25-a181-183a9ffbf4d4\"}\n[2026-05-27 18:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4926c519-81cb-435e-a7db-e0cf00a90c14\",\"trace_id\":\"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c\"}\n[2026-05-27 18:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4926c519-81cb-435e-a7db-e0cf00a90c14\",\"trace_id\":\"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c\"}\n[2026-05-27 18:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"93963a2f-85d9-4cc5-a249-82262b8edaf8\",\"trace_id\":\"df8fcf0f-7260-45ec-ae3b-ed556bad8a69\"}\n[2026-05-27 18:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"93963a2f-85d9-4cc5-a249-82262b8edaf8\",\"trace_id\":\"df8fcf0f-7260-45ec-ae3b-ed556bad8a69\"}\n[2026-05-27 18:35:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e99c406d-511d-4593-8539-0264e7b2172b\",\"trace_id\":\"47f52e14-dd88-428e-86af-8b252ac156b6\"}\n[2026-05-27 18:35:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e99c406d-511d-4593-8539-0264e7b2172b\",\"trace_id\":\"47f52e14-dd88-428e-86af-8b252ac156b6\"}\n[2026-05-27 18:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e6d0ee61-1297-496e-84a2-8b81855e39b3\",\"trace_id\":\"90f74038-8fbd-4634-95d1-6b8f9eefd7b4\"}\n[2026-05-27 18:40:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e6d0ee61-1297-496e-84a2-8b81855e39b3\",\"trace_id\":\"90f74038-8fbd-4634-95d1-6b8f9eefd7b4\"}\n[2026-05-27 18:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"75258aa9-6788-4051-963b-914c871bbc26\",\"trace_id\":\"86b6f05b-9049-4e3c-a73e-6585c75750ae\"}\n[2026-05-27 18:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"75258aa9-6788-4051-963b-914c871bbc26\",\"trace_id\":\"86b6f05b-9049-4e3c-a73e-6585c75750ae\"}\n[2026-05-27 18:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"881c5f9a-d1aa-40a1-bda0-090fed822fea\",\"trace_id\":\"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924\"}\n[2026-05-27 18:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"881c5f9a-d1aa-40a1-bda0-090fed822fea\",\"trace_id\":\"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924\"}\n[2026-05-27 18:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"991ee58d-b151-4462-bda3-94ca8d8aae80\",\"trace_id\":\"6bbdfd15-e1f3-40da-8f33-3486b4fab01c\"}\n[2026-05-27 18:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"991ee58d-b151-4462-bda3-94ca8d8aae80\",\"trace_id\":\"6bbdfd15-e1f3-40da-8f33-3486b4fab01c\"}\n[2026-05-27 22:30:28] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b435dbfd-b566-49d2-b863-abb69460900c\",\"trace_id\":\"89070b16-0908-46c7-9804-46717c0893a2\"}\n[2026-05-27 22:30:29] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b435dbfd-b566-49d2-b863-abb69460900c\",\"trace_id\":\"89070b16-0908-46c7-9804-46717c0893a2\"}\n[2026-05-28 02:35:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"54253196-1163-4487-bfe0-d66ab8141278\",\"trace_id\":\"e624f869-6e34-4a09-9aa6-318387902ae0\"}\n[2026-05-28 02:35:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"54253196-1163-4487-bfe0-d66ab8141278\",\"trace_id\":\"e624f869-6e34-4a09-9aa6-318387902ae0\"}\n[2026-05-28 03:40:19] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a\",\"trace_id\":\"6718951a-b93d-4018-9306-1d2a9fc08947\"}\n[2026-05-28 03:40:20] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a\",\"trace_id\":\"6718951a-b93d-4018-9306-1d2a9fc08947\"}\n[2026-05-28 05:15:19] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"40a9b437-3914-4fc9-bc30-0928dc6ca2e4\",\"trace_id\":\"71233b87-f2d0-4aaa-9b89-4cf0bdab4416\"}\n[2026-05-28 05:15:19] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"40a9b437-3914-4fc9-bc30-0928dc6ca2e4\",\"trace_id\":\"71233b87-f2d0-4aaa-9b89-4cf0bdab4416\"}\n[2026-05-28 05:45:32] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"df61a176-ebde-4287-a6b3-4c279ecbef90\",\"trace_id\":\"16d667db-a08d-416f-a0b6-de6aa91d95e6\"}\n[2026-05-28 05:45:32] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"df61a176-ebde-4287-a6b3-4c279ecbef90\",\"trace_id\":\"16d667db-a08d-416f-a0b6-de6aa91d95e6\"}\n[2026-05-28 05:50:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dc4ad47f-f741-4320-89f3-69916d6c0e48\",\"trace_id\":\"ffcdcb94-a2d5-4277-add2-eb2449b5c82e\"}\n[2026-05-28 05:50:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dc4ad47f-f741-4320-89f3-69916d6c0e48\",\"trace_id\":\"ffcdcb94-a2d5-4277-add2-eb2449b5c82e\"}\n[2026-05-28 05:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4be209e1-8831-49f4-8d94-62852512812a\",\"trace_id\":\"a2e0926d-7628-43b2-9a24-68beddc58f0e\"}\n[2026-05-28 05:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4be209e1-8831-49f4-8d94-62852512812a\",\"trace_id\":\"a2e0926d-7628-43b2-9a24-68beddc58f0e\"}\n[2026-05-28 06:00:13] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"42647b91-48cd-4506-9527-60b7dd707516\",\"trace_id\":\"30f5a8fd-b99c-477f-b44c-6d13d2df15e4\"}\n[2026-05-28 06:00:16] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"42647b91-48cd-4506-9527-60b7dd707516\",\"trace_id\":\"30f5a8fd-b99c-477f-b44c-6d13d2df15e4\"}\n[2026-05-28 06:05:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e98fd078-62a9-4496-bd8f-9a5b209b7938\",\"trace_id\":\"f2c87487-7772-4543-b931-fdf7095a50b2\"}\n[2026-05-28 06:05:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e98fd078-62a9-4496-bd8f-9a5b209b7938\",\"trace_id\":\"f2c87487-7772-4543-b931-fdf7095a50b2\"}\n[2026-05-28 06:10:16] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"92743ccb-6275-4aef-948a-962d2116f793\",\"trace_id\":\"6ac60160-dc90-4b6c-8e90-2bcb842751d7\"}\n[2026-05-28 06:10:16] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"92743ccb-6275-4aef-948a-962d2116f793\",\"trace_id\":\"6ac60160-dc90-4b6c-8e90-2bcb842751d7\"}\n[2026-05-28 06:15:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e36a54ec-35c2-4440-8fd3-b88b4690fcb8\",\"trace_id\":\"d5e1a237-9248-4fde-a2a4-e24c61267db9\"}\n[2026-05-28 06:15:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e36a54ec-35c2-4440-8fd3-b88b4690fcb8\",\"trace_id\":\"d5e1a237-9248-4fde-a2a4-e24c61267db9\"}","depth":4,"on_screen":true,"value":"[2026-05-27 13:05:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:05:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1\",\"trace_id\":\"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2\"}\n[2026-05-27 13:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"6dc799a7-0011-4b3c-9183-c73877223828\",\"trace_id\":\"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8\"}\n[2026-05-27 13:15:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:15:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"840a5e9f-5fef-4932-85d2-876ddc758c19\",\"trace_id\":\"fd0c3a93-dda1-4ca1-a823-55d77f0271ca\"}\n[2026-05-27 13:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4e9b89c1-a0c1-4369-8086-2efe70950237\",\"trace_id\":\"7d59d839-f1e1-4e3a-adc1-49f1b4fce100\"}\n[2026-05-27 13:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5c3ed540-7c94-41c8-bba7-d01e501b2332\",\"trace_id\":\"e5b586a5-7254-45a3-87d5-401967ed8a8a\"}\n[2026-05-27 13:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2dcb409c-6853-429e-baaf-7cc597c49d43\",\"trace_id\":\"1bae4e62-379a-468c-bdf9-ef7b41eca049\"}\n[2026-05-27 13:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d52d1000-fb4c-48c4-87f5-a3494b56a097\",\"trace_id\":\"d99ea8d9-0d8f-4127-8a10-879a6df41ee1\"}\n[2026-05-27 13:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b7d66b7a-1bc1-4633-8a6a-d5a392790150\",\"trace_id\":\"074b4cf7-05f8-4163-a37d-d76987ea742b\"}\n[2026-05-27 13:45:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0ef6ca00-983f-4d34-9814-9a2fc915d23a\",\"trace_id\":\"8c7e8482-a18c-455e-ba03-42041aeea1a7\"}\n[2026-05-27 13:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:50:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5ca99c11-c230-405a-bb61-0ea9dc937b3d\",\"trace_id\":\"5121ba3e-fe89-4a1c-987a-1085cb3990b6\"}\n[2026-05-27 13:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 13:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c12033b6-6d2c-44a7-88fd-0c669e10be3d\",\"trace_id\":\"c727f14f-f90e-4ba3-9cd3-95564ba7be74\"}\n[2026-05-27 14:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1\",\"trace_id\":\"6b3e70de-a653-42cc-affe-282ccc6e59b6\"}\n[2026-05-27 14:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"904b5a32-5054-4030-916f-ff9f60ec7c50\",\"trace_id\":\"d958f1ee-53e9-446a-80ac-1d3b166ad180\"}\n[2026-05-27 14:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:10:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"9d237419-aa02-4b84-8cca-8c933842ac37\",\"trace_id\":\"3096f218-7435-4f90-816f-83c4312ac9b0\"}\n[2026-05-27 14:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03\",\"trace_id\":\"0ec9150f-5b53-4a24-9d46-0cdc6562cf16\"}\n[2026-05-27 14:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"3170c42f-65c5-41bb-841d-c53f6dc654df\",\"trace_id\":\"851a7189-9c7f-4869-acd7-b09fb063dcc4\"}\n[2026-05-27 14:25:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:25:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7ec8e37a-1c31-4749-8f12-55aa317ef9dc\",\"trace_id\":\"ff576832-1124-4d49-8372-a3883fbb4cb0\"}\n[2026-05-27 14:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"73bee313-076a-4dd9-8abc-9213b58b2047\",\"trace_id\":\"223d997a-9432-4961-b8be-d5957f267a3f\"}\n[2026-05-27 14:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c741d7ef-29cc-409d-80e0-7ed48aadfdeb\",\"trace_id\":\"baf78c34-74c2-4cf1-9896-6331d6c2050d\"}\n[2026-05-27 14:40:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d17e6eef-b2ef-4235-80d6-1f518906fdb4\",\"trace_id\":\"e86e2fa3-614c-42bd-a407-e0f6d80e671a\"}\n[2026-05-27 14:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"bcd826dd-5d01-488a-ac33-dc28e6d2556a\",\"trace_id\":\"42c53199-ae59-48d4-b0db-943b012e30a0\"}\n[2026-05-27 14:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"fb481673-5f44-4563-bf53-a5d952364435\",\"trace_id\":\"cd9209ef-1c44-4f69-bef7-6b5b241f91cb\"}\n[2026-05-27 14:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 14:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0fa5e254-19b3-4241-a882-00a40e017029\",\"trace_id\":\"b543b0d5-db95-4ac1-864f-e43cf9bd00a0\"}\n[2026-05-27 15:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:00:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2d20a074-d8ff-46ea-a5a9-ec6b543783b7\",\"trace_id\":\"4627c651-1735-4951-88ca-1a9000a37aa1\"}\n[2026-05-27 15:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"34785a9b-c614-46f0-903f-67c1c7062657\",\"trace_id\":\"ba1a08b1-b45f-4167-9159-7e740a2eea45\"}\n[2026-05-27 15:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"74635790-bc93-4f49-809c-5a21704d47c0\",\"trace_id\":\"274de77d-905b-468b-93df-9cc92d47eba0\"}\n[2026-05-27 15:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"f88d8f66-03cc-48fb-a534-9416bf3a2046\",\"trace_id\":\"a7e41d95-7d2b-44d5-a4ae-2108ccd03583\"}\n[2026-05-27 15:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:20:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"cecf9fb3-9e74-4407-b784-8c4be3f31ded\",\"trace_id\":\"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef\"}\n[2026-05-27 15:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d30f3a08-80c8-48ed-982c-5cff4e4f4632\",\"trace_id\":\"5b2152a1-7057-4929-845a-8bd022cc2a4d\"}\n[2026-05-27 15:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"8504ec51-daa6-4401-9ef2-3e1a0efa73a9\",\"trace_id\":\"a3765887-9f45-430e-aad8-a20afb8586e2\"}\n[2026-05-27 15:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:35:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"965956e7-0a20-408e-85f6-3d47b24f9385\",\"trace_id\":\"972a9269-7970-4270-b9ff-00151e5e77cc\"}\n[2026-05-27 15:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:40:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469\",\"trace_id\":\"f103a326-846e-492c-b698-be40cabb38f5\"}\n[2026-05-27 15:45:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"d565dc8f-dcb4-4623-8ba8-f12572e56ab4\",\"trace_id\":\"8e516418-4511-45b8-bd83-512fb2ca60d5\"}\n[2026-05-27 15:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"91458bd1-bc59-484c-aea6-bd06082164c1\",\"trace_id\":\"e324b622-3072-407f-b5e9-a98c1187830d\"}\n[2026-05-27 15:55:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 15:55:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"43a6bc90-2cd7-4630-9c73-e0e0348e261b\",\"trace_id\":\"b9b372e8-1333-4c21-985e-fb09f1aa710c\"}\n[2026-05-27 16:00:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a8b87f56-3e28-420f-a213-21aa8a8fbfaf\",\"trace_id\":\"2fc20f74-145a-42de-bc9a-941731eb3c97\"}\n[2026-05-27 16:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"083ba166-b085-4c8b-9332-b79d352c38b6\",\"trace_id\":\"d7d31309-fad3-4c76-b24d-09945f44dbed\"}\n[2026-05-27 16:10:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09\",\"trace_id\":\"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa\"}\n[2026-05-27 16:15:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}\n[2026-05-27 16:15:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"895ae2b0-72ce-4fae-a0f7-0632a597c328\",\"trace_id\":\"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971\"}\n[2026-05-27 16:20:13] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2428f3a1-ffe7-47f6-9daa-3b37f277efe3\",\"trace_id\":\"273bfee4-22a7-4ae9-85be-912f0f7d5ae5\"}\n[2026-05-27 16:20:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2428f3a1-ffe7-47f6-9daa-3b37f277efe3\",\"trace_id\":\"273bfee4-22a7-4ae9-85be-912f0f7d5ae5\"}\n[2026-05-27 16:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0af368dd-50a6-4ae1-b384-a47434232748\",\"trace_id\":\"a50d2d41-72b2-44b9-8c21-da2fe7cd2350\"}\n[2026-05-27 16:25:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0af368dd-50a6-4ae1-b384-a47434232748\",\"trace_id\":\"a50d2d41-72b2-44b9-8c21-da2fe7cd2350\"}\n[2026-05-27 16:30:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7d75d5d3-7f4e-480b-beff-573be45c7d53\",\"trace_id\":\"844a2d8a-624f-442a-9354-e4fcef280e0c\"}\n[2026-05-27 16:30:13] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7d75d5d3-7f4e-480b-beff-573be45c7d53\",\"trace_id\":\"844a2d8a-624f-442a-9354-e4fcef280e0c\"}\n[2026-05-27 16:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"6d53364f-05f6-4d96-aac6-b1aab4359504\",\"trace_id\":\"48efd308-178d-41ad-b1b2-1a09bc49ab01\"}\n[2026-05-27 16:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"6d53364f-05f6-4d96-aac6-b1aab4359504\",\"trace_id\":\"48efd308-178d-41ad-b1b2-1a09bc49ab01\"}\n[2026-05-27 16:40:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"261d2acb-2d4e-47bd-a6cc-baefeb5675c4\",\"trace_id\":\"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0\"}\n[2026-05-27 16:40:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"261d2acb-2d4e-47bd-a6cc-baefeb5675c4\",\"trace_id\":\"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0\"}\n[2026-05-27 16:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b758c907-bbea-4bda-8f8d-028da9da8ce4\",\"trace_id\":\"655a6ccc-e93f-4757-8abc-e6f6dd1f6755\"}\n[2026-05-27 16:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b758c907-bbea-4bda-8f8d-028da9da8ce4\",\"trace_id\":\"655a6ccc-e93f-4757-8abc-e6f6dd1f6755\"}\n[2026-05-27 16:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"7a56f511-63a9-44f4-8917-be2add000b25\",\"trace_id\":\"70a3f353-bd37-479f-a3a4-090724cd4899\"}\n[2026-05-27 16:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"7a56f511-63a9-44f4-8917-be2add000b25\",\"trace_id\":\"70a3f353-bd37-479f-a3a4-090724cd4899\"}\n[2026-05-27 16:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5fa5a4be-c55a-4614-95e7-f92676eaeb32\",\"trace_id\":\"fcf37132-7a76-4dd4-8e90-ad50e74d299f\"}\n[2026-05-27 16:55:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5fa5a4be-c55a-4614-95e7-f92676eaeb32\",\"trace_id\":\"fcf37132-7a76-4dd4-8e90-ad50e74d299f\"}\n[2026-05-27 17:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4fcee8ab-7c17-4474-9070-83132139cec5\",\"trace_id\":\"29069de8-5869-4e80-8eea-609e70d99bee\"}\n[2026-05-27 17:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4fcee8ab-7c17-4474-9070-83132139cec5\",\"trace_id\":\"29069de8-5869-4e80-8eea-609e70d99bee\"}\n[2026-05-27 17:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"537b7577-77ba-4896-ad4a-dcedd1877d3b\",\"trace_id\":\"6d3ed1b0-6fcd-4128-a479-27658f328642\"}\n[2026-05-27 17:05:09] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"537b7577-77ba-4896-ad4a-dcedd1877d3b\",\"trace_id\":\"6d3ed1b0-6fcd-4128-a479-27658f328642\"}\n[2026-05-27 17:10:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"0704fdc6-7b13-48fc-8471-d5b02041e7a0\",\"trace_id\":\"071d0b93-27b5-437b-bdce-747dc0b6e427\"}\n[2026-05-27 17:10:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"0704fdc6-7b13-48fc-8471-d5b02041e7a0\",\"trace_id\":\"071d0b93-27b5-437b-bdce-747dc0b6e427\"}\n[2026-05-27 17:15:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"15ebdda0-0fa5-41c3-a1f3-495298d02859\",\"trace_id\":\"3ff4f628-3477-4362-8093-175b6655f290\"}\n[2026-05-27 17:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"15ebdda0-0fa5-41c3-a1f3-495298d02859\",\"trace_id\":\"3ff4f628-3477-4362-8093-175b6655f290\"}\n[2026-05-27 17:20:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f\",\"trace_id\":\"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6\"}\n[2026-05-27 17:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f\",\"trace_id\":\"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6\"}\n[2026-05-27 17:25:15] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"2dab25b9-1a2d-417d-a88c-b086be423fa9\",\"trace_id\":\"e4339f70-a4e7-44ec-836b-a779aefbef3e\"}\n[2026-05-27 17:25:15] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"2dab25b9-1a2d-417d-a88c-b086be423fa9\",\"trace_id\":\"e4339f70-a4e7-44ec-836b-a779aefbef3e\"}\n[2026-05-27 17:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a\",\"trace_id\":\"b80243e5-07bb-41e6-b3a1-26d27a4f13f0\"}\n[2026-05-27 17:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a\",\"trace_id\":\"b80243e5-07bb-41e6-b3a1-26d27a4f13f0\"}\n[2026-05-27 17:35:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dddd5b95-8803-4a70-af03-8a7174ca4a2b\",\"trace_id\":\"176bc409-1338-47d7-96bc-d6205480794d\"}\n[2026-05-27 17:35:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dddd5b95-8803-4a70-af03-8a7174ca4a2b\",\"trace_id\":\"176bc409-1338-47d7-96bc-d6205480794d\"}\n[2026-05-27 17:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4771c3be-e28d-4547-bc94-e2285ba51620\",\"trace_id\":\"3355584d-0daa-4a8a-b121-9482a0db6783\"}\n[2026-05-27 17:40:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4771c3be-e28d-4547-bc94-e2285ba51620\",\"trace_id\":\"3355584d-0daa-4a8a-b121-9482a0db6783\"}\n[2026-05-27 17:45:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"78d44e98-0241-4e33-b3d0-6adfb2a368fb\",\"trace_id\":\"465221db-a122-4ec4-9746-14e9fd0fa5fc\"}\n[2026-05-27 17:45:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"78d44e98-0241-4e33-b3d0-6adfb2a368fb\",\"trace_id\":\"465221db-a122-4ec4-9746-14e9fd0fa5fc\"}\n[2026-05-27 17:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"1d211a89-6b11-4803-a6b7-2e24f6710a52\",\"trace_id\":\"b24cc5f0-e0c7-4840-bd3a-0174203a2f72\"}\n[2026-05-27 17:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"1d211a89-6b11-4803-a6b7-2e24f6710a52\",\"trace_id\":\"b24cc5f0-e0c7-4840-bd3a-0174203a2f72\"}\n[2026-05-27 17:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"28c49b47-30c8-4d9a-83cd-b7d31b14bb37\",\"trace_id\":\"14d95738-e32a-4694-b2a8-8c327237940b\"}\n[2026-05-27 17:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"28c49b47-30c8-4d9a-83cd-b7d31b14bb37\",\"trace_id\":\"14d95738-e32a-4694-b2a8-8c327237940b\"}\n[2026-05-27 18:00:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"42220f4c-481c-46bd-ae41-326184f16003\",\"trace_id\":\"3f2ed406-60ed-4941-883b-cb8a456b1b10\"}\n[2026-05-27 18:00:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"42220f4c-481c-46bd-ae41-326184f16003\",\"trace_id\":\"3f2ed406-60ed-4941-883b-cb8a456b1b10\"}\n[2026-05-27 18:05:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"93db33bd-d0bd-46ea-8030-ae24b36ae963\",\"trace_id\":\"4696955a-81e1-4105-b8dd-61ff7a4f5584\"}\n[2026-05-27 18:05:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"93db33bd-d0bd-46ea-8030-ae24b36ae963\",\"trace_id\":\"4696955a-81e1-4105-b8dd-61ff7a4f5584\"}\n[2026-05-27 18:10:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b0ede837-0cc5-479a-ab75-db5c4101476f\",\"trace_id\":\"d7f0855f-051c-41e2-b67c-0afb6a0c294e\"}\n[2026-05-27 18:10:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b0ede837-0cc5-479a-ab75-db5c4101476f\",\"trace_id\":\"d7f0855f-051c-41e2-b67c-0afb6a0c294e\"}\n[2026-05-27 18:15:09] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b1d06246-dd9a-482e-bca8-658da1b25c37\",\"trace_id\":\"9146c9f3-e05f-489d-95f4-5f2c3b988440\"}\n[2026-05-27 18:15:10] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b1d06246-dd9a-482e-bca8-658da1b25c37\",\"trace_id\":\"9146c9f3-e05f-489d-95f4-5f2c3b988440\"}\n[2026-05-27 18:20:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e2a28a86-e3c3-46c3-88c5-209198f50961\",\"trace_id\":\"9e56205b-18a8-4a25-a181-183a9ffbf4d4\"}\n[2026-05-27 18:20:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e2a28a86-e3c3-46c3-88c5-209198f50961\",\"trace_id\":\"9e56205b-18a8-4a25-a181-183a9ffbf4d4\"}\n[2026-05-27 18:25:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4926c519-81cb-435e-a7db-e0cf00a90c14\",\"trace_id\":\"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c\"}\n[2026-05-27 18:25:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4926c519-81cb-435e-a7db-e0cf00a90c14\",\"trace_id\":\"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c\"}\n[2026-05-27 18:30:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"93963a2f-85d9-4cc5-a249-82262b8edaf8\",\"trace_id\":\"df8fcf0f-7260-45ec-ae3b-ed556bad8a69\"}\n[2026-05-27 18:30:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"93963a2f-85d9-4cc5-a249-82262b8edaf8\",\"trace_id\":\"df8fcf0f-7260-45ec-ae3b-ed556bad8a69\"}\n[2026-05-27 18:35:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e99c406d-511d-4593-8539-0264e7b2172b\",\"trace_id\":\"47f52e14-dd88-428e-86af-8b252ac156b6\"}\n[2026-05-27 18:35:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e99c406d-511d-4593-8539-0264e7b2172b\",\"trace_id\":\"47f52e14-dd88-428e-86af-8b252ac156b6\"}\n[2026-05-27 18:40:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e6d0ee61-1297-496e-84a2-8b81855e39b3\",\"trace_id\":\"90f74038-8fbd-4634-95d1-6b8f9eefd7b4\"}\n[2026-05-27 18:40:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e6d0ee61-1297-496e-84a2-8b81855e39b3\",\"trace_id\":\"90f74038-8fbd-4634-95d1-6b8f9eefd7b4\"}\n[2026-05-27 18:45:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"75258aa9-6788-4051-963b-914c871bbc26\",\"trace_id\":\"86b6f05b-9049-4e3c-a73e-6585c75750ae\"}\n[2026-05-27 18:45:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"75258aa9-6788-4051-963b-914c871bbc26\",\"trace_id\":\"86b6f05b-9049-4e3c-a73e-6585c75750ae\"}\n[2026-05-27 18:50:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"881c5f9a-d1aa-40a1-bda0-090fed822fea\",\"trace_id\":\"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924\"}\n[2026-05-27 18:50:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"881c5f9a-d1aa-40a1-bda0-090fed822fea\",\"trace_id\":\"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924\"}\n[2026-05-27 18:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"991ee58d-b151-4462-bda3-94ca8d8aae80\",\"trace_id\":\"6bbdfd15-e1f3-40da-8f33-3486b4fab01c\"}\n[2026-05-27 18:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"991ee58d-b151-4462-bda3-94ca8d8aae80\",\"trace_id\":\"6bbdfd15-e1f3-40da-8f33-3486b4fab01c\"}\n[2026-05-27 22:30:28] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"b435dbfd-b566-49d2-b863-abb69460900c\",\"trace_id\":\"89070b16-0908-46c7-9804-46717c0893a2\"}\n[2026-05-27 22:30:29] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"b435dbfd-b566-49d2-b863-abb69460900c\",\"trace_id\":\"89070b16-0908-46c7-9804-46717c0893a2\"}\n[2026-05-28 02:35:11] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"54253196-1163-4487-bfe0-d66ab8141278\",\"trace_id\":\"e624f869-6e34-4a09-9aa6-318387902ae0\"}\n[2026-05-28 02:35:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"54253196-1163-4487-bfe0-d66ab8141278\",\"trace_id\":\"e624f869-6e34-4a09-9aa6-318387902ae0\"}\n[2026-05-28 03:40:19] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a\",\"trace_id\":\"6718951a-b93d-4018-9306-1d2a9fc08947\"}\n[2026-05-28 03:40:20] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a\",\"trace_id\":\"6718951a-b93d-4018-9306-1d2a9fc08947\"}\n[2026-05-28 05:15:19] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"40a9b437-3914-4fc9-bc30-0928dc6ca2e4\",\"trace_id\":\"71233b87-f2d0-4aaa-9b89-4cf0bdab4416\"}\n[2026-05-28 05:15:19] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"40a9b437-3914-4fc9-bc30-0928dc6ca2e4\",\"trace_id\":\"71233b87-f2d0-4aaa-9b89-4cf0bdab4416\"}\n[2026-05-28 05:45:32] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"df61a176-ebde-4287-a6b3-4c279ecbef90\",\"trace_id\":\"16d667db-a08d-416f-a0b6-de6aa91d95e6\"}\n[2026-05-28 05:45:32] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"df61a176-ebde-4287-a6b3-4c279ecbef90\",\"trace_id\":\"16d667db-a08d-416f-a0b6-de6aa91d95e6\"}\n[2026-05-28 05:50:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"dc4ad47f-f741-4320-89f3-69916d6c0e48\",\"trace_id\":\"ffcdcb94-a2d5-4277-add2-eb2449b5c82e\"}\n[2026-05-28 05:50:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"dc4ad47f-f741-4320-89f3-69916d6c0e48\",\"trace_id\":\"ffcdcb94-a2d5-4277-add2-eb2449b5c82e\"}\n[2026-05-28 05:55:10] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"4be209e1-8831-49f4-8d94-62852512812a\",\"trace_id\":\"a2e0926d-7628-43b2-9a24-68beddc58f0e\"}\n[2026-05-28 05:55:11] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"4be209e1-8831-49f4-8d94-62852512812a\",\"trace_id\":\"a2e0926d-7628-43b2-9a24-68beddc58f0e\"}\n[2026-05-28 06:00:13] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"42647b91-48cd-4506-9527-60b7dd707516\",\"trace_id\":\"30f5a8fd-b99c-477f-b44c-6d13d2df15e4\"}\n[2026-05-28 06:00:16] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"42647b91-48cd-4506-9527-60b7dd707516\",\"trace_id\":\"30f5a8fd-b99c-477f-b44c-6d13d2df15e4\"}\n[2026-05-28 06:05:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e98fd078-62a9-4496-bd8f-9a5b209b7938\",\"trace_id\":\"f2c87487-7772-4543-b931-fdf7095a50b2\"}\n[2026-05-28 06:05:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e98fd078-62a9-4496-bd8f-9a5b209b7938\",\"trace_id\":\"f2c87487-7772-4543-b931-fdf7095a50b2\"}\n[2026-05-28 06:10:16] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"92743ccb-6275-4aef-948a-962d2116f793\",\"trace_id\":\"6ac60160-dc90-4b6c-8e90-2bcb842751d7\"}\n[2026-05-28 06:10:16] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"92743ccb-6275-4aef-948a-962d2116f793\",\"trace_id\":\"6ac60160-dc90-4b6c-8e90-2bcb842751d7\"}\n[2026-05-28 06:15:12] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"e36a54ec-35c2-4440-8fd3-b88b4690fcb8\",\"trace_id\":\"d5e1a237-9248-4fde-a2a4-e24c61267db9\"}\n[2026-05-28 06:15:12] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e36a54ec-35c2-4440-8fd3-b88b4690fcb8\",\"trace_id\":\"d5e1a237-9248-4fde-a2a4-e24c61267db9\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5140412069093217780
|
-4525387198509650472
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
70
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 13:05:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:05:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"85e3a9eb-5c86-4c99-803d-6703ff0ce4a1","trace_id":"71f20ee9-e1b3-4f64-a6ca-6f6b40f175f2"}
[2026-05-27 13:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"6dc799a7-0011-4b3c-9183-c73877223828","trace_id":"ef5f346b-4c1b-4bfc-b4b3-e427b81ad6c8"}
[2026-05-27 13:15:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:15:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"840a5e9f-5fef-4932-85d2-876ddc758c19","trace_id":"fd0c3a93-dda1-4ca1-a823-55d77f0271ca"}
[2026-05-27 13:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4e9b89c1-a0c1-4369-8086-2efe70950237","trace_id":"7d59d839-f1e1-4e3a-adc1-49f1b4fce100"}
[2026-05-27 13:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5c3ed540-7c94-41c8-bba7-d01e501b2332","trace_id":"e5b586a5-7254-45a3-87d5-401967ed8a8a"}
[2026-05-27 13:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2dcb409c-6853-429e-baaf-7cc597c49d43","trace_id":"1bae4e62-379a-468c-bdf9-ef7b41eca049"}
[2026-05-27 13:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d52d1000-fb4c-48c4-87f5-a3494b56a097","trace_id":"d99ea8d9-0d8f-4127-8a10-879a6df41ee1"}
[2026-05-27 13:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b7d66b7a-1bc1-4633-8a6a-d5a392790150","trace_id":"074b4cf7-05f8-4163-a37d-d76987ea742b"}
[2026-05-27 13:45:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0ef6ca00-983f-4d34-9814-9a2fc915d23a","trace_id":"8c7e8482-a18c-455e-ba03-42041aeea1a7"}
[2026-05-27 13:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:50:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5ca99c11-c230-405a-bb61-0ea9dc937b3d","trace_id":"5121ba3e-fe89-4a1c-987a-1085cb3990b6"}
[2026-05-27 13:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 13:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c12033b6-6d2c-44a7-88fd-0c669e10be3d","trace_id":"c727f14f-f90e-4ba3-9cd3-95564ba7be74"}
[2026-05-27 14:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7eeaaf2a-b8b7-4656-bdbe-eca8990237c1","trace_id":"6b3e70de-a653-42cc-affe-282ccc6e59b6"}
[2026-05-27 14:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"904b5a32-5054-4030-916f-ff9f60ec7c50","trace_id":"d958f1ee-53e9-446a-80ac-1d3b166ad180"}
[2026-05-27 14:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:10:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"9d237419-aa02-4b84-8cca-8c933842ac37","trace_id":"3096f218-7435-4f90-816f-83c4312ac9b0"}
[2026-05-27 14:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dcf3fa08-a873-4b17-aa06-2a28c3e7fc03","trace_id":"0ec9150f-5b53-4a24-9d46-0cdc6562cf16"}
[2026-05-27 14:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"3170c42f-65c5-41bb-841d-c53f6dc654df","trace_id":"851a7189-9c7f-4869-acd7-b09fb063dcc4"}
[2026-05-27 14:25:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:25:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7ec8e37a-1c31-4749-8f12-55aa317ef9dc","trace_id":"ff576832-1124-4d49-8372-a3883fbb4cb0"}
[2026-05-27 14:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"73bee313-076a-4dd9-8abc-9213b58b2047","trace_id":"223d997a-9432-4961-b8be-d5957f267a3f"}
[2026-05-27 14:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c741d7ef-29cc-409d-80e0-7ed48aadfdeb","trace_id":"baf78c34-74c2-4cf1-9896-6331d6c2050d"}
[2026-05-27 14:40:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d17e6eef-b2ef-4235-80d6-1f518906fdb4","trace_id":"e86e2fa3-614c-42bd-a407-e0f6d80e671a"}
[2026-05-27 14:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"bcd826dd-5d01-488a-ac33-dc28e6d2556a","trace_id":"42c53199-ae59-48d4-b0db-943b012e30a0"}
[2026-05-27 14:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"fb481673-5f44-4563-bf53-a5d952364435","trace_id":"cd9209ef-1c44-4f69-bef7-6b5b241f91cb"}
[2026-05-27 14:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 14:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0fa5e254-19b3-4241-a882-00a40e017029","trace_id":"b543b0d5-db95-4ac1-864f-e43cf9bd00a0"}
[2026-05-27 15:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:00:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2d20a074-d8ff-46ea-a5a9-ec6b543783b7","trace_id":"4627c651-1735-4951-88ca-1a9000a37aa1"}
[2026-05-27 15:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"34785a9b-c614-46f0-903f-67c1c7062657","trace_id":"ba1a08b1-b45f-4167-9159-7e740a2eea45"}
[2026-05-27 15:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"74635790-bc93-4f49-809c-5a21704d47c0","trace_id":"274de77d-905b-468b-93df-9cc92d47eba0"}
[2026-05-27 15:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"f88d8f66-03cc-48fb-a534-9416bf3a2046","trace_id":"a7e41d95-7d2b-44d5-a4ae-2108ccd03583"}
[2026-05-27 15:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:20:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"cecf9fb3-9e74-4407-b784-8c4be3f31ded","trace_id":"e3c18f01-c6e2-4014-b582-7bf9ed0b82ef"}
[2026-05-27 15:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d30f3a08-80c8-48ed-982c-5cff4e4f4632","trace_id":"5b2152a1-7057-4929-845a-8bd022cc2a4d"}
[2026-05-27 15:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"8504ec51-daa6-4401-9ef2-3e1a0efa73a9","trace_id":"a3765887-9f45-430e-aad8-a20afb8586e2"}
[2026-05-27 15:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:35:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"965956e7-0a20-408e-85f6-3d47b24f9385","trace_id":"972a9269-7970-4270-b9ff-00151e5e77cc"}
[2026-05-27 15:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:40:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a14c89c5-4019-4a4a-80d4-f4aaf4b7a469","trace_id":"f103a326-846e-492c-b698-be40cabb38f5"}
[2026-05-27 15:45:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"d565dc8f-dcb4-4623-8ba8-f12572e56ab4","trace_id":"8e516418-4511-45b8-bd83-512fb2ca60d5"}
[2026-05-27 15:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"91458bd1-bc59-484c-aea6-bd06082164c1","trace_id":"e324b622-3072-407f-b5e9-a98c1187830d"}
[2026-05-27 15:55:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 15:55:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"43a6bc90-2cd7-4630-9c73-e0e0348e261b","trace_id":"b9b372e8-1333-4c21-985e-fb09f1aa710c"}
[2026-05-27 16:00:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a8b87f56-3e28-420f-a213-21aa8a8fbfaf","trace_id":"2fc20f74-145a-42de-bc9a-941731eb3c97"}
[2026-05-27 16:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"083ba166-b085-4c8b-9332-b79d352c38b6","trace_id":"d7d31309-fad3-4c76-b24d-09945f44dbed"}
[2026-05-27 16:10:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dd2d7f5d-89d2-44e5-b397-fbe4829e0b09","trace_id":"d370bbe4-f40d-4a33-84a1-ca32ad7d38fa"}
[2026-05-27 16:15:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
[2026-05-27 16:15:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"895ae2b0-72ce-4fae-a0f7-0632a597c328","trace_id":"1c3b5fd2-3cb5-4cb5-9530-6574b1c87971"}
[2026-05-27 16:20:13] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2428f3a1-ffe7-47f6-9daa-3b37f277efe3","trace_id":"273bfee4-22a7-4ae9-85be-912f0f7d5ae5"}
[2026-05-27 16:20:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2428f3a1-ffe7-47f6-9daa-3b37f277efe3","trace_id":"273bfee4-22a7-4ae9-85be-912f0f7d5ae5"}
[2026-05-27 16:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0af368dd-50a6-4ae1-b384-a47434232748","trace_id":"a50d2d41-72b2-44b9-8c21-da2fe7cd2350"}
[2026-05-27 16:25:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0af368dd-50a6-4ae1-b384-a47434232748","trace_id":"a50d2d41-72b2-44b9-8c21-da2fe7cd2350"}
[2026-05-27 16:30:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7d75d5d3-7f4e-480b-beff-573be45c7d53","trace_id":"844a2d8a-624f-442a-9354-e4fcef280e0c"}
[2026-05-27 16:30:13] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7d75d5d3-7f4e-480b-beff-573be45c7d53","trace_id":"844a2d8a-624f-442a-9354-e4fcef280e0c"}
[2026-05-27 16:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"6d53364f-05f6-4d96-aac6-b1aab4359504","trace_id":"48efd308-178d-41ad-b1b2-1a09bc49ab01"}
[2026-05-27 16:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"6d53364f-05f6-4d96-aac6-b1aab4359504","trace_id":"48efd308-178d-41ad-b1b2-1a09bc49ab01"}
[2026-05-27 16:40:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"261d2acb-2d4e-47bd-a6cc-baefeb5675c4","trace_id":"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0"}
[2026-05-27 16:40:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"261d2acb-2d4e-47bd-a6cc-baefeb5675c4","trace_id":"3e3de4eb-2f97-4ff9-ada8-4639e484c6d0"}
[2026-05-27 16:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b758c907-bbea-4bda-8f8d-028da9da8ce4","trace_id":"655a6ccc-e93f-4757-8abc-e6f6dd1f6755"}
[2026-05-27 16:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b758c907-bbea-4bda-8f8d-028da9da8ce4","trace_id":"655a6ccc-e93f-4757-8abc-e6f6dd1f6755"}
[2026-05-27 16:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"7a56f511-63a9-44f4-8917-be2add000b25","trace_id":"70a3f353-bd37-479f-a3a4-090724cd4899"}
[2026-05-27 16:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"7a56f511-63a9-44f4-8917-be2add000b25","trace_id":"70a3f353-bd37-479f-a3a4-090724cd4899"}
[2026-05-27 16:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5fa5a4be-c55a-4614-95e7-f92676eaeb32","trace_id":"fcf37132-7a76-4dd4-8e90-ad50e74d299f"}
[2026-05-27 16:55:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5fa5a4be-c55a-4614-95e7-f92676eaeb32","trace_id":"fcf37132-7a76-4dd4-8e90-ad50e74d299f"}
[2026-05-27 17:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4fcee8ab-7c17-4474-9070-83132139cec5","trace_id":"29069de8-5869-4e80-8eea-609e70d99bee"}
[2026-05-27 17:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4fcee8ab-7c17-4474-9070-83132139cec5","trace_id":"29069de8-5869-4e80-8eea-609e70d99bee"}
[2026-05-27 17:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"537b7577-77ba-4896-ad4a-dcedd1877d3b","trace_id":"6d3ed1b0-6fcd-4128-a479-27658f328642"}
[2026-05-27 17:05:09] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"537b7577-77ba-4896-ad4a-dcedd1877d3b","trace_id":"6d3ed1b0-6fcd-4128-a479-27658f328642"}
[2026-05-27 17:10:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"0704fdc6-7b13-48fc-8471-d5b02041e7a0","trace_id":"071d0b93-27b5-437b-bdce-747dc0b6e427"}
[2026-05-27 17:10:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"0704fdc6-7b13-48fc-8471-d5b02041e7a0","trace_id":"071d0b93-27b5-437b-bdce-747dc0b6e427"}
[2026-05-27 17:15:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"15ebdda0-0fa5-41c3-a1f3-495298d02859","trace_id":"3ff4f628-3477-4362-8093-175b6655f290"}
[2026-05-27 17:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"15ebdda0-0fa5-41c3-a1f3-495298d02859","trace_id":"3ff4f628-3477-4362-8093-175b6655f290"}
[2026-05-27 17:20:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f","trace_id":"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6"}
[2026-05-27 17:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b89eb6c2-85e0-487e-a8e5-b6ba1edae61f","trace_id":"44f2f03c-68e8-4dee-9bbc-b75ba9c61ff6"}
[2026-05-27 17:25:15] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"2dab25b9-1a2d-417d-a88c-b086be423fa9","trace_id":"e4339f70-a4e7-44ec-836b-a779aefbef3e"}
[2026-05-27 17:25:15] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"2dab25b9-1a2d-417d-a88c-b086be423fa9","trace_id":"e4339f70-a4e7-44ec-836b-a779aefbef3e"}
[2026-05-27 17:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a","trace_id":"b80243e5-07bb-41e6-b3a1-26d27a4f13f0"}
[2026-05-27 17:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"a577c0ae-abdd-4f23-8cbc-b2760ffbaa5a","trace_id":"b80243e5-07bb-41e6-b3a1-26d27a4f13f0"}
[2026-05-27 17:35:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dddd5b95-8803-4a70-af03-8a7174ca4a2b","trace_id":"176bc409-1338-47d7-96bc-d6205480794d"}
[2026-05-27 17:35:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dddd5b95-8803-4a70-af03-8a7174ca4a2b","trace_id":"176bc409-1338-47d7-96bc-d6205480794d"}
[2026-05-27 17:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4771c3be-e28d-4547-bc94-e2285ba51620","trace_id":"3355584d-0daa-4a8a-b121-9482a0db6783"}
[2026-05-27 17:40:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4771c3be-e28d-4547-bc94-e2285ba51620","trace_id":"3355584d-0daa-4a8a-b121-9482a0db6783"}
[2026-05-27 17:45:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"78d44e98-0241-4e33-b3d0-6adfb2a368fb","trace_id":"465221db-a122-4ec4-9746-14e9fd0fa5fc"}
[2026-05-27 17:45:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"78d44e98-0241-4e33-b3d0-6adfb2a368fb","trace_id":"465221db-a122-4ec4-9746-14e9fd0fa5fc"}
[2026-05-27 17:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"1d211a89-6b11-4803-a6b7-2e24f6710a52","trace_id":"b24cc5f0-e0c7-4840-bd3a-0174203a2f72"}
[2026-05-27 17:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"1d211a89-6b11-4803-a6b7-2e24f6710a52","trace_id":"b24cc5f0-e0c7-4840-bd3a-0174203a2f72"}
[2026-05-27 17:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"28c49b47-30c8-4d9a-83cd-b7d31b14bb37","trace_id":"14d95738-e32a-4694-b2a8-8c327237940b"}
[2026-05-27 17:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"28c49b47-30c8-4d9a-83cd-b7d31b14bb37","trace_id":"14d95738-e32a-4694-b2a8-8c327237940b"}
[2026-05-27 18:00:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"42220f4c-481c-46bd-ae41-326184f16003","trace_id":"3f2ed406-60ed-4941-883b-cb8a456b1b10"}
[2026-05-27 18:00:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"42220f4c-481c-46bd-ae41-326184f16003","trace_id":"3f2ed406-60ed-4941-883b-cb8a456b1b10"}
[2026-05-27 18:05:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"93db33bd-d0bd-46ea-8030-ae24b36ae963","trace_id":"4696955a-81e1-4105-b8dd-61ff7a4f5584"}
[2026-05-27 18:05:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"93db33bd-d0bd-46ea-8030-ae24b36ae963","trace_id":"4696955a-81e1-4105-b8dd-61ff7a4f5584"}
[2026-05-27 18:10:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b0ede837-0cc5-479a-ab75-db5c4101476f","trace_id":"d7f0855f-051c-41e2-b67c-0afb6a0c294e"}
[2026-05-27 18:10:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b0ede837-0cc5-479a-ab75-db5c4101476f","trace_id":"d7f0855f-051c-41e2-b67c-0afb6a0c294e"}
[2026-05-27 18:15:09] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b1d06246-dd9a-482e-bca8-658da1b25c37","trace_id":"9146c9f3-e05f-489d-95f4-5f2c3b988440"}
[2026-05-27 18:15:10] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b1d06246-dd9a-482e-bca8-658da1b25c37","trace_id":"9146c9f3-e05f-489d-95f4-5f2c3b988440"}
[2026-05-27 18:20:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e2a28a86-e3c3-46c3-88c5-209198f50961","trace_id":"9e56205b-18a8-4a25-a181-183a9ffbf4d4"}
[2026-05-27 18:20:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e2a28a86-e3c3-46c3-88c5-209198f50961","trace_id":"9e56205b-18a8-4a25-a181-183a9ffbf4d4"}
[2026-05-27 18:25:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4926c519-81cb-435e-a7db-e0cf00a90c14","trace_id":"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c"}
[2026-05-27 18:25:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4926c519-81cb-435e-a7db-e0cf00a90c14","trace_id":"2b0c6c7c-a0e0-4baf-944b-ee9a028f512c"}
[2026-05-27 18:30:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"93963a2f-85d9-4cc5-a249-82262b8edaf8","trace_id":"df8fcf0f-7260-45ec-ae3b-ed556bad8a69"}
[2026-05-27 18:30:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"93963a2f-85d9-4cc5-a249-82262b8edaf8","trace_id":"df8fcf0f-7260-45ec-ae3b-ed556bad8a69"}
[2026-05-27 18:35:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e99c406d-511d-4593-8539-0264e7b2172b","trace_id":"47f52e14-dd88-428e-86af-8b252ac156b6"}
[2026-05-27 18:35:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e99c406d-511d-4593-8539-0264e7b2172b","trace_id":"47f52e14-dd88-428e-86af-8b252ac156b6"}
[2026-05-27 18:40:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e6d0ee61-1297-496e-84a2-8b81855e39b3","trace_id":"90f74038-8fbd-4634-95d1-6b8f9eefd7b4"}
[2026-05-27 18:40:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e6d0ee61-1297-496e-84a2-8b81855e39b3","trace_id":"90f74038-8fbd-4634-95d1-6b8f9eefd7b4"}
[2026-05-27 18:45:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"75258aa9-6788-4051-963b-914c871bbc26","trace_id":"86b6f05b-9049-4e3c-a73e-6585c75750ae"}
[2026-05-27 18:45:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"75258aa9-6788-4051-963b-914c871bbc26","trace_id":"86b6f05b-9049-4e3c-a73e-6585c75750ae"}
[2026-05-27 18:50:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"881c5f9a-d1aa-40a1-bda0-090fed822fea","trace_id":"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924"}
[2026-05-27 18:50:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"881c5f9a-d1aa-40a1-bda0-090fed822fea","trace_id":"52cd3ce8-4d30-4e77-b2ed-0b4548cf2924"}
[2026-05-27 18:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"991ee58d-b151-4462-bda3-94ca8d8aae80","trace_id":"6bbdfd15-e1f3-40da-8f33-3486b4fab01c"}
[2026-05-27 18:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"991ee58d-b151-4462-bda3-94ca8d8aae80","trace_id":"6bbdfd15-e1f3-40da-8f33-3486b4fab01c"}
[2026-05-27 22:30:28] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"b435dbfd-b566-49d2-b863-abb69460900c","trace_id":"89070b16-0908-46c7-9804-46717c0893a2"}
[2026-05-27 22:30:29] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"b435dbfd-b566-49d2-b863-abb69460900c","trace_id":"89070b16-0908-46c7-9804-46717c0893a2"}
[2026-05-28 02:35:11] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"[CREDIT_CARD]-bfe0-d66ab8141278","trace_id":"e624f869-6e34-4a09-9aa6-318387902ae0"}
[2026-05-28 02:35:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"[CREDIT_CARD]-bfe0-d66ab8141278","trace_id":"e624f869-6e34-4a09-9aa6-318387902ae0"}
[2026-05-28 03:40:19] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a","trace_id":"6718951a-b93d-4018-9306-1d2a9fc08947"}
[2026-05-28 03:40:20] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"c7c8023f-f3fb-4643-a9a1-7bba4b5df98a","trace_id":"6718951a-b93d-4018-9306-1d2a9fc08947"}
[2026-05-28 05:15:19] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"40a9b437-3914-4fc9-bc30-0928dc6ca2e4","trace_id":"71233b87-f2d0-4aaa-9b89-4cf0bdab4416"}
[2026-05-28 05:15:19] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"40a9b437-3914-4fc9-bc30-0928dc6ca2e4","trace_id":"71233b87-f2d0-4aaa-9b89-4cf0bdab4416"}
[2026-05-28 05:45:32] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"df61a176-ebde-4287-a6b3-4c279ecbef90","trace_id":"16d667db-a08d-416f-a0b6-de6aa91d95e6"}
[2026-05-28 05:45:32] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"df61a176-ebde-4287-a6b3-4c279ecbef90","trace_id":"16d667db-a08d-416f-a0b6-de6aa91d95e6"}
[2026-05-28 05:50:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"dc4ad47f-f741-4320-89f3-69916d6c0e48","trace_id":"ffcdcb94-a2d5-4277-add2-eb2449b5c82e"}
[2026-05-28 05:50:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"dc4ad47f-f741-4320-89f3-69916d6c0e48","trace_id":"ffcdcb94-a2d5-4277-add2-eb2449b5c82e"}
[2026-05-28 05:55:10] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"4be209e1-8831-49f4-8d94-62852512812a","trace_id":"a2e0926d-7628-43b2-9a24-68beddc58f0e"}
[2026-05-28 05:55:11] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"4be209e1-8831-49f4-8d94-62852512812a","trace_id":"a2e0926d-7628-43b2-9a24-68beddc58f0e"}
[2026-05-28 06:00:13] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"42647b91-48cd-4506-9527-60b7dd707516","trace_id":"30f5a8fd-b99c-477f-b44c-6d13d2df15e4"}
[2026-05-28 06:00:16] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"42647b91-48cd-4506-9527-60b7dd707516","trace_id":"30f5a8fd-b99c-477f-b44c-6d13d2df15e4"}
[2026-05-28 06:05:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e98fd078-62a9-4496-bd8f-9a5b209b7938","trace_id":"f2c87487-7772-4543-b931-fdf7095a50b2"}
[2026-05-28 06:05:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e98fd078-62a9-4496-bd8f-9a5b209b7938","trace_id":"f2c87487-7772-4543-b931-fdf7095a50b2"}
[2026-05-28 06:10:16] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"92743ccb-6275-4aef-948a-962d2116f793","trace_id":"6ac60160-dc90-4b6c-8e90-2bcb842751d7"}
[2026-05-28 06:10:16] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"92743ccb-6275-4aef-948a-962d2116f793","trace_id":"6ac60160-dc90-4b6c-8e90-2bcb842751d7"}
[2026-05-28 06:15:12] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"e36a54ec-35c2-4440-8fd3-b88b4690fcb8","trace_id":"d5e1a237-9248-4fde-a2a4-e24c61267db9"}
[2026-05-28 06:15:12] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e36a54ec-35c2-4440-8fd3-b88b4690fcb8","trace_id":"d5e1a237-9248-4fde-a2a4-e24c61267db9"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
79073
|
NULL
|
NULL
|
NULL
|
|
79079
|
2780
|
13
|
2026-05-28T06:17:30.161899+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779949050161_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
70
Previous Highlighted Error
Next Highlighted Error
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.10055866,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"70","depth":4,"bounds":{"left":0.7273936,"top":0.10055866,"width":0.009973404,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.7390292,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.7463431,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5698951489277158132
|
-6434374888888824949
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
70
Previous Highlighted Error
Next Highlighted Error
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
80540
|
2806
|
35
|
2026-05-28T07:25:43.337801+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779953143337_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
12
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Crm\Profile;
use League\Fractal\Resource\Collection;
use League\Fractal\TransformerAbstract;
class FieldTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'options',
];
private ?Profile $profile;
private bool $noDefault = false;
public function __construct(?Profile $profile = null)
{
$this->profile = $profile;
}
public function setNoDefault(bool $noDefault)
{
$this->noDefault = $noDefault;
}
public function transform(Field $field): array
{
$defaultValue = $field->default_value;
$type = $field->type;
if (\is_bool($defaultValue)) {
$defaultValue = (bool) $defaultValue;
}
return [
'id' => $field->id_string,
'objectType' => $field->object_type,
'label' => $field->label,
'fieldName' => $field->crm_provider_id,
'type' => $type,
'enabled' => $field->is_selectable,
'length' => $field->length,
'defaultValue' => $defaultValue,
];
}
public function includeOptions(Field $field): ?Collection
{
if ($field->type === Field::TYPE_PICKLIST && $values = $field->values) {
// This gets a little complex. We need to check if this field has any record type constraints for the profile.
if (
$this->profile
&& ($recordType = $this->profile->{$field->object_type . 'RecordType'})
&& $recordType->fieldValues()->where('crm_field_id', $field->id)->exists()
) {
// Fetch the values specific for this record type.
$values = $recordType->fieldValues()->where('crm_field_id', $field->id)->get();
}
if ($this->noDefault) {
foreach ($values as $value) {
$value->is_default = false;
}
}
return $this->collection($values, new FieldValueTransformer());
}
return null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
[2026-05-28 06:20:17] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"eb2ceaa9-3c2c-4565-89e3-be8707228009","trace_id":"e4034d7f-baee-4459-b6df-a81bbf82dfad"}
[2026-05-28 06:20:17] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"eb2ceaa9-3c2c-4565-89e3-be8707228009","trace_id":"e4034d7f-baee-4459-b6df-a81bbf82dfad"}
[2026-05-28 06:25:30] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"82a859be-9a1f-43a5-981d-f87e9b9e2f3d","trace_id":"7b6d36b6-49db-4488-9177-b36654415069"}
[2026-05-28 06:25:31] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"82a859be-9a1f-43a5-981d-f87e9b9e2f3d","trace_id":"7b6d36b6-49db-4488-9177-b36654415069"}
[2026-05-28 06:30:33] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"ab88e81f-7665-401b-8545-8406ed036667","trace_id":"fb9bfe8c-5d30-46ff-a594-878f108fdaea"}
[2026-05-28 06:30:34] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"ab88e81f-7665-401b-8545-8406ed036667","trace_id":"fb9bfe8c-5d30-46ff-a594-878f108fdaea"}
[2026-05-28 06:35:18] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5cec298f-3536-47d7-a138-568e9f8f5c30","trace_id":"b2ce6c4a-3676-41f0-9026-297eccddb870"}
[2026-05-28 06:35:18] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5cec298f-3536-47d7-a138-568e9f8f5c30","trace_id":"b2ce6c4a-3676-41f0-9026-297eccddb870"}
[2026-05-28 06:41:34] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"df35489c-e090-41f3-b01b-a698412aad6c","trace_id":"5d08188d-e44b-489c-885b-4fe99d19a30e"}
[2026-05-28 06:41:35] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"df35489c-e090-41f3-b01b-a698412aad6c","trace_id":"5d08188d-e44b-489c-885b-4fe99d19a30e"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"12","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Crm\\Profile;\nuse League\\Fractal\\Resource\\Collection;\nuse League\\Fractal\\TransformerAbstract;\n\nclass FieldTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'options',\n ];\n private ?Profile $profile;\n\n private bool $noDefault = false;\n\n public function __construct(?Profile $profile = null)\n {\n $this->profile = $profile;\n }\n\n public function setNoDefault(bool $noDefault)\n {\n $this->noDefault = $noDefault;\n }\n\n public function transform(Field $field): array\n {\n $defaultValue = $field->default_value;\n $type = $field->type;\n\n if (\\is_bool($defaultValue)) {\n $defaultValue = (bool) $defaultValue;\n }\n\n return [\n 'id' => $field->id_string,\n 'objectType' => $field->object_type,\n 'label' => $field->label,\n 'fieldName' => $field->crm_provider_id,\n 'type' => $type,\n 'enabled' => $field->is_selectable,\n 'length' => $field->length,\n 'defaultValue' => $defaultValue,\n ];\n }\n\n public function includeOptions(Field $field): ?Collection\n {\n if ($field->type === Field::TYPE_PICKLIST && $values = $field->values) {\n // This gets a little complex. We need to check if this field has any record type constraints for the profile.\n if (\n $this->profile\n && ($recordType = $this->profile->{$field->object_type . 'RecordType'})\n && $recordType->fieldValues()->where('crm_field_id', $field->id)->exists()\n ) {\n // Fetch the values specific for this record type.\n $values = $recordType->fieldValues()->where('crm_field_id', $field->id)->get();\n }\n\n if ($this->noDefault) {\n foreach ($values as $value) {\n $value->is_default = false;\n }\n }\n\n return $this->collection($values, new FieldValueTransformer());\n }\n\n return null;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Crm\\Profile;\nuse League\\Fractal\\Resource\\Collection;\nuse League\\Fractal\\TransformerAbstract;\n\nclass FieldTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'options',\n ];\n private ?Profile $profile;\n\n private bool $noDefault = false;\n\n public function __construct(?Profile $profile = null)\n {\n $this->profile = $profile;\n }\n\n public function setNoDefault(bool $noDefault)\n {\n $this->noDefault = $noDefault;\n }\n\n public function transform(Field $field): array\n {\n $defaultValue = $field->default_value;\n $type = $field->type;\n\n if (\\is_bool($defaultValue)) {\n $defaultValue = (bool) $defaultValue;\n }\n\n return [\n 'id' => $field->id_string,\n 'objectType' => $field->object_type,\n 'label' => $field->label,\n 'fieldName' => $field->crm_provider_id,\n 'type' => $type,\n 'enabled' => $field->is_selectable,\n 'length' => $field->length,\n 'defaultValue' => $defaultValue,\n ];\n }\n\n public function includeOptions(Field $field): ?Collection\n {\n if ($field->type === Field::TYPE_PICKLIST && $values = $field->values) {\n // This gets a little complex. We need to check if this field has any record type constraints for the profile.\n if (\n $this->profile\n && ($recordType = $this->profile->{$field->object_type . 'RecordType'})\n && $recordType->fieldValues()->where('crm_field_id', $field->id)->exists()\n ) {\n // Fetch the values specific for this record type.\n $values = $recordType->fieldValues()->where('crm_field_id', $field->id)->get();\n }\n\n if ($this->noDefault) {\n foreach ($values as $value) {\n $value->is_default = false;\n }\n }\n\n return $this->collection($values, new FieldValueTransformer());\n }\n\n return null;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-28 06:20:17] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"eb2ceaa9-3c2c-4565-89e3-be8707228009\",\"trace_id\":\"e4034d7f-baee-4459-b6df-a81bbf82dfad\"}\n[2026-05-28 06:20:17] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"eb2ceaa9-3c2c-4565-89e3-be8707228009\",\"trace_id\":\"e4034d7f-baee-4459-b6df-a81bbf82dfad\"}\n[2026-05-28 06:25:30] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"82a859be-9a1f-43a5-981d-f87e9b9e2f3d\",\"trace_id\":\"7b6d36b6-49db-4488-9177-b36654415069\"}\n[2026-05-28 06:25:31] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"82a859be-9a1f-43a5-981d-f87e9b9e2f3d\",\"trace_id\":\"7b6d36b6-49db-4488-9177-b36654415069\"}\n[2026-05-28 06:30:33] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"ab88e81f-7665-401b-8545-8406ed036667\",\"trace_id\":\"fb9bfe8c-5d30-46ff-a594-878f108fdaea\"}\n[2026-05-28 06:30:34] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"ab88e81f-7665-401b-8545-8406ed036667\",\"trace_id\":\"fb9bfe8c-5d30-46ff-a594-878f108fdaea\"}\n[2026-05-28 06:35:18] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5cec298f-3536-47d7-a138-568e9f8f5c30\",\"trace_id\":\"b2ce6c4a-3676-41f0-9026-297eccddb870\"}\n[2026-05-28 06:35:18] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5cec298f-3536-47d7-a138-568e9f8f5c30\",\"trace_id\":\"b2ce6c4a-3676-41f0-9026-297eccddb870\"}\n[2026-05-28 06:41:34] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"df35489c-e090-41f3-b01b-a698412aad6c\",\"trace_id\":\"5d08188d-e44b-489c-885b-4fe99d19a30e\"}\n[2026-05-28 06:41:35] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"df35489c-e090-41f3-b01b-a698412aad6c\",\"trace_id\":\"5d08188d-e44b-489c-885b-4fe99d19a30e\"}","depth":4,"on_screen":true,"value":"[2026-05-28 06:20:17] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"eb2ceaa9-3c2c-4565-89e3-be8707228009\",\"trace_id\":\"e4034d7f-baee-4459-b6df-a81bbf82dfad\"}\n[2026-05-28 06:20:17] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"eb2ceaa9-3c2c-4565-89e3-be8707228009\",\"trace_id\":\"e4034d7f-baee-4459-b6df-a81bbf82dfad\"}\n[2026-05-28 06:25:30] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"82a859be-9a1f-43a5-981d-f87e9b9e2f3d\",\"trace_id\":\"7b6d36b6-49db-4488-9177-b36654415069\"}\n[2026-05-28 06:25:31] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"82a859be-9a1f-43a5-981d-f87e9b9e2f3d\",\"trace_id\":\"7b6d36b6-49db-4488-9177-b36654415069\"}\n[2026-05-28 06:30:33] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"ab88e81f-7665-401b-8545-8406ed036667\",\"trace_id\":\"fb9bfe8c-5d30-46ff-a594-878f108fdaea\"}\n[2026-05-28 06:30:34] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"ab88e81f-7665-401b-8545-8406ed036667\",\"trace_id\":\"fb9bfe8c-5d30-46ff-a594-878f108fdaea\"}\n[2026-05-28 06:35:18] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"5cec298f-3536-47d7-a138-568e9f8f5c30\",\"trace_id\":\"b2ce6c4a-3676-41f0-9026-297eccddb870\"}\n[2026-05-28 06:35:18] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"5cec298f-3536-47d7-a138-568e9f8f5c30\",\"trace_id\":\"b2ce6c4a-3676-41f0-9026-297eccddb870\"}\n[2026-05-28 06:41:34] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358994\n)\n {\"correlation_id\":\"df35489c-e090-41f3-b01b-a698412aad6c\",\"trace_id\":\"5d08188d-e44b-489c-885b-4fe99d19a30e\"}\n[2026-05-28 06:41:35] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"df35489c-e090-41f3-b01b-a698412aad6c\",\"trace_id\":\"5d08188d-e44b-489c-885b-4fe99d19a30e\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8092765758302445417
|
-8056134594893481487
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
12
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Crm\Profile;
use League\Fractal\Resource\Collection;
use League\Fractal\TransformerAbstract;
class FieldTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'options',
];
private ?Profile $profile;
private bool $noDefault = false;
public function __construct(?Profile $profile = null)
{
$this->profile = $profile;
}
public function setNoDefault(bool $noDefault)
{
$this->noDefault = $noDefault;
}
public function transform(Field $field): array
{
$defaultValue = $field->default_value;
$type = $field->type;
if (\is_bool($defaultValue)) {
$defaultValue = (bool) $defaultValue;
}
return [
'id' => $field->id_string,
'objectType' => $field->object_type,
'label' => $field->label,
'fieldName' => $field->crm_provider_id,
'type' => $type,
'enabled' => $field->is_selectable,
'length' => $field->length,
'defaultValue' => $defaultValue,
];
}
public function includeOptions(Field $field): ?Collection
{
if ($field->type === Field::TYPE_PICKLIST && $values = $field->values) {
// This gets a little complex. We need to check if this field has any record type constraints for the profile.
if (
$this->profile
&& ($recordType = $this->profile->{$field->object_type . 'RecordType'})
&& $recordType->fieldValues()->where('crm_field_id', $field->id)->exists()
) {
// Fetch the values specific for this record type.
$values = $recordType->fieldValues()->where('crm_field_id', $field->id)->get();
}
if ($this->noDefault) {
foreach ($values as $value) {
$value->is_default = false;
}
}
return $this->collection($values, new FieldValueTransformer());
}
return null;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
[2026-05-28 06:20:17] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"eb2ceaa9-3c2c-4565-89e3-be8707228009","trace_id":"e4034d7f-baee-4459-b6df-a81bbf82dfad"}
[2026-05-28 06:20:17] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"eb2ceaa9-3c2c-4565-89e3-be8707228009","trace_id":"e4034d7f-baee-4459-b6df-a81bbf82dfad"}
[2026-05-28 06:25:30] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"82a859be-9a1f-43a5-981d-f87e9b9e2f3d","trace_id":"7b6d36b6-49db-4488-9177-b36654415069"}
[2026-05-28 06:25:31] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"82a859be-9a1f-43a5-981d-f87e9b9e2f3d","trace_id":"7b6d36b6-49db-4488-9177-b36654415069"}
[2026-05-28 06:30:33] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"ab88e81f-7665-401b-8545-8406ed036667","trace_id":"fb9bfe8c-5d30-46ff-a594-878f108fdaea"}
[2026-05-28 06:30:34] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"ab88e81f-7665-401b-8545-8406ed036667","trace_id":"fb9bfe8c-5d30-46ff-a594-878f108fdaea"}
[2026-05-28 06:35:18] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"5cec298f-3536-47d7-a138-568e9f8f5c30","trace_id":"b2ce6c4a-3676-41f0-9026-297eccddb870"}
[2026-05-28 06:35:18] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"5cec298f-3536-47d7-a138-568e9f8f5c30","trace_id":"b2ce6c4a-3676-41f0-9026-297eccddb870"}
[2026-05-28 06:41:34] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358994
)
{"correlation_id":"df35489c-e090-41f3-b01b-a698412aad6c","trace_id":"5d08188d-e44b-489c-885b-4fe99d19a30e"}
[2026-05-28 06:41:35] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"df35489c-e090-41f3-b01b-a698412aad6c","trace_id":"5d08188d-e44b-489c-885b-4fe99d19a30e"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
80577
|
2809
|
15
|
2026-05-28T07:29:37.402521+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779953377402_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rnostolFV faVsco.is ~$2 JY-20915-fix-missing-heade rnostolFV faVsco.is ~$2 JY-20915-fix-missing-header-text-rela)pho l0005.ongpheo matl.criyPmaxio.ongPYlkoule"medd"sueamine.oneHwowercalonephp queue.phpsdlestorce.onesamconephp secure-headers.phgwpservices.ongppslack.ohephptimezones.ohgphp view.ohephp webhook-server.oho> E contrib→darabasrontcent>Ulangnode modtles hhrary toon© SyncMailbox.phpphp api.phpecrmeontrober.orLayoutltonsrormer.onycayoutenulytronstotmer.oncomposer,isonDockerfileC) TextRelayServiceTest.phpZ.envpuourc tuncczon sync arrayGGGлаSд558а8Snailbox = config( key: 'jiminny.google_text_user'):SexpectedAlias = config( key: '"iminny.deploy region' «a= 'eu" ? 'catch-all-eu' : 'catch-all':SexpectedHost = config( key: ""iminny.google text host):Log::info message:" (TextRelayService) Starting sync'.mozloox ay shdeloox'expected alias' => SexpectedAlias,'expecceonos => sexpecteohoscSservzce = schis->qerservzcelsaaz200x)HSnessaochiscory = Sthis->occhzscory Sservice(Illuninate|Suppont\Facades\Log::channel( channet: 'custon channel')->info(*SnessagcHistory •Snessaocids =05foreach (Smessagehistory as shistories) 1Shessages = Shistonies->nessagesAdded 29 11:foreach (Smessages as Snessage) 1messanelidunnessadeonescaceo%egmhtsorssorsurrent Enus nonsentserusce naithoy, messadeiid Seypecter.uas, Soyoa→tnanstant mualiid> @ resourcesv hroutesphe api.phpphe api_v2.phpne concole nbohcustomer.aplonpMoamsioo0o.oneMo haalth obomuoro celcoweo.onMaweb chepp weonook.one> scriptev(ln storage> O apo>@ debugbar> &a frameworkviloasrcitionore0 audio wav=aravelcacamnit xmScalavedTeyt = TeytRelay..rhenelcoenail_provider_id', Smessageld)-›firstO:+ScalavedTeyt ees oulihSrelayedText = TextRelay::createemail provider' => TextRelay::PROVIDER_GSUITE'email provider id' => Smessageld'status" => TextRelay::STATUSPROCESSING.JDASiob = new EnailTextRelay(Smessageld, SrelavedText):Siob->onQueue( queue: Constants::QUEUE EMATLS) :dsoarch100)Log::info( message: '(TextRelavService) Successfully dispatched nessage'. fmessage_1d a> Snessaceiotextrelayd a> Srelavedtexto»ldaueue constants.clrr.telaravel.log x SF (iminny@localhost)HS Jocal (jiminny(blocalhost)& console [PROD# console FulstartHistoryId => 358994correlazon1d: 8/085900-4017-4305-9810-187e909e2tS0race20: /0603606-4700-4488-[CREDIT_CARD]-17826-85-28 86:25:37 Loca4WFu: Snessagchzscorycorrelation1d: [CREDIT_CARD]-9870-487690902450Trac6-10: 0605606-4900-4488-97-056654415869-17876-85-78 86:88 omuetfts oaranelh story wnes messaneaddedctartHistorydil358091"correlation_1d:"ab88e81f-1665-4810-8545-8486ed836667, trace_1d: fb9bfe8c-5030-46ff-a594-878f188fdaea"}Array{"correlation_id":"ab88e81f-7665-481b-8545-8486ed836667", *trace_1d":"fb9bfe8c-5d30-46ff-a594-878f188fdaea")(2826-85-28 86:35:18) local.INF0: SparansArrayhistoryTypes => messageAddecnhonWo = 39077"correlation_id":"Scec298f-3536-47d7-a138-568e9f8fSc39*, *trace_id":"b2ce6c4a-3676-41f0-9826-297eccddb879"-thon1.0g29 94-26-1811 Jocol0Net: Snossaach stom.3-3616-4148-9826-297ecc00087/84517826-85328 86:4334 oc0Fl: Soaransmistoryivoes,startHistoryd 8> 358994conneaton r""eshioe-anorana-hitheador vhachouooace ae Sank kkcahun-n8oc-885h- Feooc Carhe"ieiheR Naehesocnsi ShaccadelstonyiRnonhnekroc-e.sherfaooch oakhe"oos L nu co moy l0.zarseTextRelayServiceTesteeendhaner hensewiet+0.can I test it locally first. if I place env from stagingYes. The service relies on a tew thinas vou'd need in place1. Credentials file The constructor expects storage/text-relay.ison (a Google service account JSON key file)Gmbtrom sthaind and oince it there2. .env values to copy from stagingGOOGLE TEXTUSERECOOGLE TEXTI HASTGOOGLE TEXT RELAY TOPIC:DEPLOY REGION:the cnail mailbox address# e.g. txt.jiminny.com or txt.staging.jiminny.comaeu or us3. Run the sunc manualiv vin tinker tinside docker 1aeo 1%b bashaegdocker exec -it docker_lamp_1 php artisan tinkeapp(\Jiminny\Services\Ma1l\TextRelayService::class)-sync():Caveat: The service reads from Gmail history and dispatches jobs to the queue. If you don't want it to actuallsend SMS, make sure QUEUE_CONNECTICN»sync is not set (or use a test queue), or just call isForCurrentEnvironntioh.Ssvc = new class extends \Jiminny\Services\Mail\TextRelayService & public function _construct(som Serviceassvc-soctServicelcontiatainny.googletextI then sinsoect a eessade canual1sasg= sonailService-susers_messages-sget(config(*iminty.google text user'), *THE_MESSAGE ID')collect(5-s0-20etPayload()-soetWeaders())-soluck("value"thut wey von can Mrity whht handare tra nehhilm oraeset do ttat mactsod alote tha ull evoc rineGadl -Ask anything (XOL)—@ eodeAdhotvP4 s...
|
NULL
|
8873838753829079385
|
NULL
|
click
|
ocr
|
NULL
|
rnostolFV faVsco.is ~$2 JY-20915-fix-missing-heade rnostolFV faVsco.is ~$2 JY-20915-fix-missing-header-text-rela)pho l0005.ongpheo matl.criyPmaxio.ongPYlkoule"medd"sueamine.oneHwowercalonephp queue.phpsdlestorce.onesamconephp secure-headers.phgwpservices.ongppslack.ohephptimezones.ohgphp view.ohephp webhook-server.oho> E contrib→darabasrontcent>Ulangnode modtles hhrary toon© SyncMailbox.phpphp api.phpecrmeontrober.orLayoutltonsrormer.onycayoutenulytronstotmer.oncomposer,isonDockerfileC) TextRelayServiceTest.phpZ.envpuourc tuncczon sync arrayGGGлаSд558а8Snailbox = config( key: 'jiminny.google_text_user'):SexpectedAlias = config( key: '"iminny.deploy region' «a= 'eu" ? 'catch-all-eu' : 'catch-all':SexpectedHost = config( key: ""iminny.google text host):Log::info message:" (TextRelayService) Starting sync'.mozloox ay shdeloox'expected alias' => SexpectedAlias,'expecceonos => sexpecteohoscSservzce = schis->qerservzcelsaaz200x)HSnessaochiscory = Sthis->occhzscory Sservice(Illuninate|Suppont\Facades\Log::channel( channet: 'custon channel')->info(*SnessagcHistory •Snessaocids =05foreach (Smessagehistory as shistories) 1Shessages = Shistonies->nessagesAdded 29 11:foreach (Smessages as Snessage) 1messanelidunnessadeonescaceo%egmhtsorssorsurrent Enus nonsentserusce naithoy, messadeiid Seypecter.uas, Soyoa→tnanstant mualiid> @ resourcesv hroutesphe api.phpphe api_v2.phpne concole nbohcustomer.aplonpMoamsioo0o.oneMo haalth obomuoro celcoweo.onMaweb chepp weonook.one> scriptev(ln storage> O apo>@ debugbar> &a frameworkviloasrcitionore0 audio wav=aravelcacamnit xmScalavedTeyt = TeytRelay..rhenelcoenail_provider_id', Smessageld)-›firstO:+ScalavedTeyt ees oulihSrelayedText = TextRelay::createemail provider' => TextRelay::PROVIDER_GSUITE'email provider id' => Smessageld'status" => TextRelay::STATUSPROCESSING.JDASiob = new EnailTextRelay(Smessageld, SrelavedText):Siob->onQueue( queue: Constants::QUEUE EMATLS) :dsoarch100)Log::info( message: '(TextRelavService) Successfully dispatched nessage'. fmessage_1d a> Snessaceiotextrelayd a> Srelavedtexto»ldaueue constants.clrr.telaravel.log x SF (iminny@localhost)HS Jocal (jiminny(blocalhost)& console [PROD# console FulstartHistoryId => 358994correlazon1d: 8/085900-4017-4305-9810-187e909e2tS0race20: /0603606-4700-4488-[CREDIT_CARD]-17826-85-28 86:25:37 Loca4WFu: Snessagchzscorycorrelation1d: [CREDIT_CARD]-9870-487690902450Trac6-10: 0605606-4900-4488-97-056654415869-17876-85-78 86:88 omuetfts oaranelh story wnes messaneaddedctartHistorydil358091"correlation_1d:"ab88e81f-1665-4810-8545-8486ed836667, trace_1d: fb9bfe8c-5030-46ff-a594-878f188fdaea"}Array{"correlation_id":"ab88e81f-7665-481b-8545-8486ed836667", *trace_1d":"fb9bfe8c-5d30-46ff-a594-878f188fdaea")(2826-85-28 86:35:18) local.INF0: SparansArrayhistoryTypes => messageAddecnhonWo = 39077"correlation_id":"Scec298f-3536-47d7-a138-568e9f8fSc39*, *trace_id":"b2ce6c4a-3676-41f0-9826-297eccddb879"-thon1.0g29 94-26-1811 Jocol0Net: Snossaach stom.3-3616-4148-9826-297ecc00087/84517826-85328 86:4334 oc0Fl: Soaransmistoryivoes,startHistoryd 8> 358994conneaton r""eshioe-anorana-hitheador vhachouooace ae Sank kkcahun-n8oc-885h- Feooc Carhe"ieiheR Naehesocnsi ShaccadelstonyiRnonhnekroc-e.sherfaooch oakhe"oos L nu co moy l0.zarseTextRelayServiceTesteeendhaner hensewiet+0.can I test it locally first. if I place env from stagingYes. The service relies on a tew thinas vou'd need in place1. Credentials file The constructor expects storage/text-relay.ison (a Google service account JSON key file)Gmbtrom sthaind and oince it there2. .env values to copy from stagingGOOGLE TEXTUSERECOOGLE TEXTI HASTGOOGLE TEXT RELAY TOPIC:DEPLOY REGION:the cnail mailbox address# e.g. txt.jiminny.com or txt.staging.jiminny.comaeu or us3. Run the sunc manualiv vin tinker tinside docker 1aeo 1%b bashaegdocker exec -it docker_lamp_1 php artisan tinkeapp(\Jiminny\Services\Ma1l\TextRelayService::class)-sync():Caveat: The service reads from Gmail history and dispatches jobs to the queue. If you don't want it to actuallsend SMS, make sure QUEUE_CONNECTICN»sync is not set (or use a test queue), or just call isForCurrentEnvironntioh.Ssvc = new class extends \Jiminny\Services\Mail\TextRelayService & public function _construct(som Serviceassvc-soctServicelcontiatainny.googletextI then sinsoect a eessade canual1sasg= sonailService-susers_messages-sget(config(*iminty.google text user'), *THE_MESSAGE ID')collect(5-s0-20etPayload()-soetWeaders())-soluck("value"thut wey von can Mrity whht handare tra nehhilm oraeset do ttat mactsod alote tha ull evoc rineGadl -Ask anything (XOL)—@ eodeAdhotvP4 s...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
80582
|
2808
|
13
|
2026-05-28T07:29:49.370647+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779953389370_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Code changed:
Hide
Sync Changes
Hide This Notification...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7676320973686270867
|
-6434304657575212661
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Code changed:
Hide
Sync Changes
Hide This Notification...
|
80580
|
NULL
|
NULL
|
NULL
|
|
80623
|
2809
|
37
|
2026-05-28T07:30:41.958640+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779953441958_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09740692,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8374335,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"bounds":{"left":0.85272604,"top":0.019952115,"width":0.062832445,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.37666222,"top":0.12529927,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.38597074,"top":0.12529927,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.39694148,"top":0.123703115,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.40425533,"top":0.123703115,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Mail;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\Log;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Jobs\\Mailbox\\EmailTextRelay;\nuse Jiminny\\Models\\TextRelay;\nuse Google\\Service\\Gmail as GoogleGmail;\n\nclass TextRelayService\n{\n public function __construct()\n {\n $credentials = storage_path('text-relay.json');\n\n abort_unless(file_exists($credentials), 422);\n\n putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);\n }\n\n /**\n * Fetch the latest messages since the last sync.\n */\n public function sync(): array\n {\n $mailbox = config('jiminny.google_text_user');\n\n $expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';\n $expectedHost = config('jiminny.google_text_host');\n\n Log::info('[TextRelayService] Starting sync', [\n 'mailbox' => $mailbox,\n 'expected_alias' => $expectedAlias,\n 'expected_host' => $expectedHost,\n ]);\n\n $service = $this->getService($mailbox);\n\n $messageHistory = $this->getHistory($service);\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));\n $messageIds = [];\n\n foreach ($messageHistory as $histories) {\n $messages = $histories->messagesAdded ?? [];\n\n foreach ($messages as $message) {\n $messageId = $message->message->id;\n\n if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {\n continue;\n }\n\n $relayedText = TextRelay::where('email_provider_id', $messageId)->first();\n\n if ($relayedText === null) {\n $relayedText = TextRelay::create([\n 'email_provider' => TextRelay::PROVIDER_GSUITE,\n 'email_provider_id' => $messageId,\n 'status' => TextRelay::STATUS_PROCESSING,\n ]);\n }\n\n $job = new EmailTextRelay($messageId, $relayedText);\n $job->onQueue(Constants::QUEUE_EMAILS);\n\n// dispatch($job);\n\n Log::info('[TextRelayService] Successfully dispatched message', [\n 'message_id' => $messageId,\n 'text_relay_id' => $relayedText->id,\n 'queue' => Constants::QUEUE_EMAILS,\n ]);\n\n $messageIds[] = $messageId;\n }\n }\n\n Log::info('[TextRelayService] Sync completed', [\n 'mailbox' => $mailbox,\n 'messages_processed' => count($messageIds),\n 'message_ids' => array_slice($messageIds, 0, 10),\n ]);\n\n return $messageIds;\n }\n\n public function getHistory(GoogleGmail $service): array\n {\n $pageToken = null;\n $topic = config('jiminny.google_text_relay_topic');\n $messages = [];\n $historyId = \\Cache::get($topic);\n\n // If we have no history stored, WatchMailboxEvents must not have run yet :/\n if ($historyId == false) {\n $historyId = $this->refreshHistoryPoint($topic);\n }\n\n $params = [\n 'historyTypes' => 'messageAdded',\n 'startHistoryId' => $historyId,\n ];\n\n do {\n try {\n if ($pageToken) {\n $params['pageToken'] = $pageToken;\n }\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));\n\n $historyResponse = $service->users_history->listUsersHistory(\n config('jiminny.google_text_user'),\n $params\n );\n\n $this->setHistoryPoint($topic, (int) $historyResponse->historyId);\n\n if ($historyResponse->getHistory()) {\n $messages = array_merge($messages, $historyResponse->getHistory());\n $pageToken = $historyResponse->getNextPageToken();\n }\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to fetch Gmail history', [\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n } while ($pageToken);\n\n return $messages;\n }\n\n protected function setHistoryPoint(string $topic, int $historyPoint): Carbon\n {\n $expiresAt = now()->addDay();\n\n \\Cache::put($topic, $historyPoint, $expiresAt);\n\n return $expiresAt;\n }\n\n public function getService(string $mailbox): GoogleGmail\n {\n $client = new \\Google_Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(GoogleGmail::GMAIL_MODIFY);\n $client->setAccessType('offline');\n $client->setSubject($mailbox);\n\n return new GoogleGmail($client);\n }\n\n public function refreshHistoryPoint(string $topic): int\n {\n $mailbox = config('jiminny.google_text_user');\n $service = $this->getService($mailbox);\n\n $watchRequest = new GoogleGmail\\WatchRequest();\n $watchRequest->setLabelIds(['INBOX']);\n $watchRequest->setLabelFilterAction('include');\n $watchRequest->setTopicName($topic);\n\n $watchResponse = $service->users->watch($mailbox, $watchRequest);\n\n //$expiryTimestamp = $watchResponse->expiration / 1000;\n $historyPoint = (int) $watchResponse->historyId;\n\n $this->setHistoryPoint($topic, $historyPoint);\n\n return $historyPoint;\n }\n\n private function isForCurrentEnvironment(\n GoogleGmail $service,\n string $mailbox,\n string $messageId,\n string $expectedAlias,\n string $expectedHost\n ): bool {\n try {\n $message = $service->users_messages->get($mailbox, $messageId);\n $headers = $message->getPayload()->getHeaders();\n\n foreach ($headers as $header) {\n if ($header->name === 'X-Gm-Original-To') {\n $matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);\n\n if (! $matches) {\n // Sanitize PII by removing plus-tag content for logging\n $sanitizedOriginalTo = explode('+', $header->value)[0];\n Log::info('[TextRelayService] Refused message', [\n 'message_id' => $messageId,\n 'original_to_sanitized' => $sanitizedOriginalTo,\n ]);\n }\n\n return $matches;\n }\n }\n\n Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [\n 'message_id' => $messageId,\n ]);\n } catch (\\Exception $e) {\n Log::error('[TextRelayService] Failed to inspect message', [\n 'message_id' => $messageId,\n 'exception' => $e->getMessage(),\n ]);\n \\Sentry::captureException($e);\n }\n\n return false;\n }\n\n private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool\n {\n $parts = explode('@', $recipient, 2);\n\n if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {\n return false;\n }\n\n $localBase = explode('+', $parts[0], 2)[0];\n\n return strcasecmp($localBase, $expectedAlias) === 0;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"Editor for custom.log","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7759981108847106831
|
-6434374888888824949
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
1
17
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Mail;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Jiminny\Component\Queue\Constants;
use Jiminny\Jobs\Mailbox\EmailTextRelay;
use Jiminny\Models\TextRelay;
use Google\Service\Gmail as GoogleGmail;
class TextRelayService
{
public function __construct()
{
$credentials = storage_path('text-relay.json');
abort_unless(file_exists($credentials), 422);
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $credentials);
}
/**
* Fetch the latest messages since the last sync.
*/
public function sync(): array
{
$mailbox = config('jiminny.google_text_user');
$expectedAlias = config('jiminny.deploy_region') === 'eu' ? 'catch-all-eu' : 'catch-all';
$expectedHost = config('jiminny.google_text_host');
Log::info('[TextRelayService] Starting sync', [
'mailbox' => $mailbox,
'expected_alias' => $expectedAlias,
'expected_host' => $expectedHost,
]);
$service = $this->getService($mailbox);
$messageHistory = $this->getHistory($service);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$messageHistory ' . PHP_EOL . print_r($messageHistory, true));
$messageIds = [];
foreach ($messageHistory as $histories) {
$messages = $histories->messagesAdded ?? [];
foreach ($messages as $message) {
$messageId = $message->message->id;
if (! $this->isForCurrentEnvironment($service, $mailbox, $messageId, $expectedAlias, $expectedHost)) {
continue;
}
$relayedText = TextRelay::where('email_provider_id', $messageId)->first();
if ($relayedText === null) {
$relayedText = TextRelay::create([
'email_provider' => TextRelay::PROVIDER_GSUITE,
'email_provider_id' => $messageId,
'status' => TextRelay::STATUS_PROCESSING,
]);
}
$job = new EmailTextRelay($messageId, $relayedText);
$job->onQueue(Constants::QUEUE_EMAILS);
// dispatch($job);
Log::info('[TextRelayService] Successfully dispatched message', [
'message_id' => $messageId,
'text_relay_id' => $relayedText->id,
'queue' => Constants::QUEUE_EMAILS,
]);
$messageIds[] = $messageId;
}
}
Log::info('[TextRelayService] Sync completed', [
'mailbox' => $mailbox,
'messages_processed' => count($messageIds),
'message_ids' => array_slice($messageIds, 0, 10),
]);
return $messageIds;
}
public function getHistory(GoogleGmail $service): array
{
$pageToken = null;
$topic = config('jiminny.google_text_relay_topic');
$messages = [];
$historyId = \Cache::get($topic);
// If we have no history stored, WatchMailboxEvents must not have run yet :/
if ($historyId == false) {
$historyId = $this->refreshHistoryPoint($topic);
}
$params = [
'historyTypes' => 'messageAdded',
'startHistoryId' => $historyId,
];
do {
try {
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$params ' . PHP_EOL . print_r($params, true));
$historyResponse = $service->users_history->listUsersHistory(
config('jiminny.google_text_user'),
$params
);
$this->setHistoryPoint($topic, (int) $historyResponse->historyId);
if ($historyResponse->getHistory()) {
$messages = array_merge($messages, $historyResponse->getHistory());
$pageToken = $historyResponse->getNextPageToken();
}
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to fetch Gmail history', [
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
} while ($pageToken);
return $messages;
}
protected function setHistoryPoint(string $topic, int $historyPoint): Carbon
{
$expiresAt = now()->addDay();
\Cache::put($topic, $historyPoint, $expiresAt);
return $expiresAt;
}
public function getService(string $mailbox): GoogleGmail
{
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(GoogleGmail::GMAIL_MODIFY);
$client->setAccessType('offline');
$client->setSubject($mailbox);
return new GoogleGmail($client);
}
public function refreshHistoryPoint(string $topic): int
{
$mailbox = config('jiminny.google_text_user');
$service = $this->getService($mailbox);
$watchRequest = new GoogleGmail\WatchRequest();
$watchRequest->setLabelIds(['INBOX']);
$watchRequest->setLabelFilterAction('include');
$watchRequest->setTopicName($topic);
$watchResponse = $service->users->watch($mailbox, $watchRequest);
//$expiryTimestamp = $watchResponse->expiration / 1000;
$historyPoint = (int) $watchResponse->historyId;
$this->setHistoryPoint($topic, $historyPoint);
return $historyPoint;
}
private function isForCurrentEnvironment(
GoogleGmail $service,
string $mailbox,
string $messageId,
string $expectedAlias,
string $expectedHost
): bool {
try {
$message = $service->users_messages->get($mailbox, $messageId);
$headers = $message->getPayload()->getHeaders();
foreach ($headers as $header) {
if ($header->name === 'X-Gm-Original-To') {
$matches = $this->matchesExpectedRecipient($header->value, $expectedAlias, $expectedHost);
if (! $matches) {
// Sanitize PII by removing plus-tag content for logging
$sanitizedOriginalTo = explode('+', $header->value)[0];
Log::info('[TextRelayService] Refused message', [
'message_id' => $messageId,
'original_to_sanitized' => $sanitizedOriginalTo,
]);
}
return $matches;
}
}
Log::warning('[TextRelayService] Refused message: missing X-Gm-Original-To header', [
'message_id' => $messageId,
]);
} catch (\Exception $e) {
Log::error('[TextRelayService] Failed to inspect message', [
'message_id' => $messageId,
'exception' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return false;
}
private function matchesExpectedRecipient(string $recipient, string $expectedAlias, string $expectedHost): bool
{
$parts = explode('@', $recipient, 2);
if (count($parts) !== 2 || strcasecmp($parts[1], $expectedHost) !== 0) {
return false;
}
$localBase = explode('+', $parts[0], 2)[0];
return strcasecmp($localBase, $expectedAlias) === 0;
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
80699
|
2811
|
35
|
2026-05-28T07:33:33.491893+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779953613491_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8043719072324535154
|
-8628527368849355612
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
rapstomViewNeweNNCCoocKet Project: faVsco.js, menu
rapstomViewNeweNNCCoocKetucioTOOI-WindowFV faVsco.|s ~$ JY-20915-fix-missing-header-text-rProinet vphe logos.phppheo matl.criyPmoxo.ongPYlkoule"medd"sueamine.oneHwowercalonephp queue.phpsdlestorce.onesamconesecure-neddcis.oiwpservices.ohgppslack.ohephptimezones.ohgphp view.ohephp webhook-server.oho> En contrit.→darabasrontcent>Elangnode modtles hhrary toon→tnanstant mualiid>@ resourcesv hroutesphe api.phpphe api_v2.phpne concole nbophe customer_api.phpMoamsioo0o.oneMo haalth obopupro cetco weo.oneMaweb cnopp weonook.one> scriptev(ln storage> O apo>@ debugbar> &a frameworkvloasrcitionore0 audio wav= custom logcamnit xmFttiePutkunrvate ka3) SyncMailbox.php x* DockerfileTextRelayServiceTest.phpclass SyncMailbox extends Command17 €24627 ФSAKYSTESOSEGROSSS*avan scrinaprorecred Sstonature ="earloox:text-relay: syne""* The console comsand descriozon.* Evac stringprotected Sdescription = 'Sync text relay's mailbox with latest nessages.':2 usagesprivate Scelaysenv.ce;public function __construct(TextRelayService SrelayService)parent::_constructo:Sthis->relayService = SrelayService:icxccure ch consore comnaheouoec tunceon nindte Vorretoy «iI/ If we don't have relay conficuration, just bail nowtcontad key:"jiminny,coogie texthost') aa= nulb) {Snessacelds= sthis-srelayseryscesysync.orSthis->Line( strina: 'Relaved * count (Snessageids) . • nessage(s). '):catch (Fycention SelhsisaserronSa.saarlascade.onSentou. cantiinesycentdalse*7o0sLX• Thu 28 May 10:33:33araveliosA console (STAGINGSF (iminny@localhost)HS.Jocal jiminny@blocalhost)# consol (PRont# console leul• 43 A YTextRelayServiceTestaner hensewietcan I test it locally first. if I place env from stagingYes, The service relies on a tew thinas wou'd need in olacel1. Credentials file The constructor expects storage/text-relay.son (a Google service account JSON key file)Gmbtrom sthaind and oince it there2. .env values to copy from stagingGooGLE TExTUSER.CAOGLE TEXT HASTGOOGLE TEXT RELAY TOPICDEPLOY REGION:the cnail mailbox address# e.g. txt.jiminny.com or txt.staging.jiminny.comaeu or us3. Run the sunc manualiv vin tinker tinside docker 1aeo 1%b bashaegdocker exec -it docker_lamp_1 php artisan tinkeapp(\Jiminny\Services\Ma1l\TextRelayService::class)-sync():Caveat: The service reads from Gmail history and dispatches jobs to the queue. If you don't want it to actuallsend SMS, make sure QUEUE_CONNECTICN»sync is not set (or use a test queue), or just call isForCurrentEnvironntioh.Ssvc = new class extends \Jiminny\Services\Mail\TextRelayService & public function _construct(som Serviceassvc-soctServicelcontiatainny.googletex)I then sinsoect aueessaaesasg= sonailService-susers_messages-sget(config(*iminty.google text user'), *THE_MESSAGE ID*)collect(5-s0-20etPayload()-soetWeaders())-soluck("value"thut wey von can Mrity whht handare tra nehhilm oraeset do ttat mactsod alote tha ull evoc rineAsk anything (XoL)-@ eodeAdhotvMndturTasmehies*4 space...
|
80697
|
NULL
|
NULL
|
NULL
|
|
80853
|
2814
|
6
|
2026-05-28T07:43:20.487550+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779954200487_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp100% C8 • Thu 28 May 10:43:20• 0DEV (docker)screenpipe"181DOCKER₴81DEV (docker)₴2-zsh8830 84-zshX5ec2-user@ip-10-30-1...O ₴6ec2-user@ip-10-30-140-...$7lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny~/Jiminny/app JY-20915-fix-missing-header-text-relay) $ co master. env.localapp/Console/Commands/JiminnyDebugCommand.phpapp/Services/Mail/TextRelayService.phpconfig/logging.phpSwitched to branch 'master'Your branch is up to date with 'origin/master'.DEVlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (master) $ git pullremote: Enumerating objects: 250, done.remote: Countingobjects: 100% (198/198), done.remote: Compressing objects: 100% (70/70), done.remote: Total 250 (delta 154), reused 138 (delta128), pack-reused 52 (from 2)Receiving objects: 100% (250/250), 99.48 KiB | 783.00 KiB/s,done.Resolving deltas: 100% (160/160), completed with 46 localobjects.From github.com:jiminny/app890e429206..828865edebmastera867cc532f..c9701224df-> origin/masterJY-20664-customer-api-exec-reports-> origin/JY-20664-customer-api-exec-reports+ 8ee439f68c...145d59435f JY-20828-user-can-manage-desktop-recording-permissions-> origin/JY-20828-user-can-manage-desktop-recording-permissions(forced update)8564b059d7..6ef3afd1d0JY-20835-mcp-enable-users-to-get-a-list-of-deals-and-their-details -> origin/JY-20835-mcp-enable-users-to-get-a-list-of-deals-and-their-details* [2ew brane. 6C3bC795af JY-20855-mcp-Es-mappings-> origin/JY-20855-mcp-ES-mappings (forced update)JY-20855-mcp-ES-mappings-BCKP-before-rebase-> origin/JY-20855-mcp-ES-mappings-BCKP-before-rebaseb3659b1290. .eb199eee578afc20af84..9e9a0f0bd4JY-20910-schedule-parallel-update-target-processing-> origin/JY-20910-schedule-parallel-update-target-processingclaude-reviewer-improvements-4-> origin/claude-reviewer-improvements-4* [new branch]master-deploy-> origin/master-deployUpdating 890e429206. .828865edebFast-forwarddatabase/mappings/mapping_activities.jsondatabase/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.php || 341312 files changed, 372 insertions(+)create mode 100644 database/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.phplukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (master) $ coJY-20915-fix-missing-header-text-relay. env.localMapp/Console/Commands/JiminnyDebugCommand.phpapp/Services/Mail/TextRelayService.phpconfig/logging.phpSwitched to branch 'JY-20915-fix-missing-header-text-relay'lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20915-fix-missing-header-text-relay) $ git merge masterUpdating 890e429206..828865edebFast-forwarddatabase/mappings/mapping_activities.jsondatabase/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.php l+++2 files changed, 372 insertions(+)create mode 100644 database/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.phplukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20915-fix-missing-header-text-relay) $ devroot@docker_lamp_1:/home/jiminny# ]...
|
NULL
|
2915798541776578802
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp100% C8 • Thu 28 May 10:43:20• 0DEV (docker)screenpipe"181DOCKER₴81DEV (docker)₴2-zsh8830 84-zshX5ec2-user@ip-10-30-1...O ₴6ec2-user@ip-10-30-140-...$7lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny~/Jiminny/app JY-20915-fix-missing-header-text-relay) $ co master. env.localapp/Console/Commands/JiminnyDebugCommand.phpapp/Services/Mail/TextRelayService.phpconfig/logging.phpSwitched to branch 'master'Your branch is up to date with 'origin/master'.DEVlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (master) $ git pullremote: Enumerating objects: 250, done.remote: Countingobjects: 100% (198/198), done.remote: Compressing objects: 100% (70/70), done.remote: Total 250 (delta 154), reused 138 (delta128), pack-reused 52 (from 2)Receiving objects: 100% (250/250), 99.48 KiB | 783.00 KiB/s,done.Resolving deltas: 100% (160/160), completed with 46 localobjects.From github.com:jiminny/app890e429206..828865edebmastera867cc532f..c9701224df-> origin/masterJY-20664-customer-api-exec-reports-> origin/JY-20664-customer-api-exec-reports+ 8ee439f68c...145d59435f JY-20828-user-can-manage-desktop-recording-permissions-> origin/JY-20828-user-can-manage-desktop-recording-permissions(forced update)8564b059d7..6ef3afd1d0JY-20835-mcp-enable-users-to-get-a-list-of-deals-and-their-details -> origin/JY-20835-mcp-enable-users-to-get-a-list-of-deals-and-their-details* [2ew brane. 6C3bC795af JY-20855-mcp-Es-mappings-> origin/JY-20855-mcp-ES-mappings (forced update)JY-20855-mcp-ES-mappings-BCKP-before-rebase-> origin/JY-20855-mcp-ES-mappings-BCKP-before-rebaseb3659b1290. .eb199eee578afc20af84..9e9a0f0bd4JY-20910-schedule-parallel-update-target-processing-> origin/JY-20910-schedule-parallel-update-target-processingclaude-reviewer-improvements-4-> origin/claude-reviewer-improvements-4* [new branch]master-deploy-> origin/master-deployUpdating 890e429206. .828865edebFast-forwarddatabase/mappings/mapping_activities.jsondatabase/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.php || 341312 files changed, 372 insertions(+)create mode 100644 database/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.phplukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (master) $ coJY-20915-fix-missing-header-text-relay. env.localMapp/Console/Commands/JiminnyDebugCommand.phpapp/Services/Mail/TextRelayService.phpconfig/logging.phpSwitched to branch 'JY-20915-fix-missing-header-text-relay'lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20915-fix-missing-header-text-relay) $ git merge masterUpdating 890e429206..828865edebFast-forwarddatabase/mappings/mapping_activities.jsondatabase/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.php l+++2 files changed, 372 insertions(+)create mode 100644 database/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.phplukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20915-fix-missing-header-text-relay) $ devroot@docker_lamp_1:/home/jiminny# ]...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
80854
|
2815
|
5
|
2026-05-28T07:43:20.590461+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779954200590_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rapstomViewNeweNNCCoocKetucioTOOI-WindowFV faVsco. rapstomViewNeweNNCCoocKetucioTOOI-WindowFV faVsco.|s ~$ JY-20915-fix-missing-header-text-rProinet vphe logos.phppheo matl.criyPmoxo.ongPYlkowie"medd"sueamine.oneHwowercalonephp queue.phpsdlestorce.onesamconephp secure-headers.phgwpservices.ohgppslack.ohephptimezones.ohgphp view.ohrphp webhook-server.oho> En contrit.→darabasrontcent>Ulangnode modues hhrary roon→tnanstant mualiid>@ resourcesv hroutesphe api.phpphe api_v2.phpne concale nbophe customer_api.phpMoamsioo0o.oneMo haalth obopupro cetco weo.oneMaweb chepp weonook.one> scriptev(ln storage> O apo>@ debugbar> &a frameworkviloasrcitionore0 audio wav= custom logcamnit xmFttiePTuthuARvatA ka3) SyncMailbox.php xockhrhttTextRelayServiceTest.phpclass SyncMailbox extends Command17 €246127 ФASA6KAKN8858*avan scrinaprorecred Sstonature = 'eartbox:text-relay: syng"?* The console comsand descriozon.* Evac stringprotected Sdescription = 'Sync text relay's mailbox with latest nessages.':2 usagesprivate Scelaysenv.ce;public function __construct(TextRelayService SrelayService)parent::_constructo:Sthis->relayService = SrelayService:icxccure ch consore comnaheouoeeTunceoh ninete voretoy «iI/ If we don't have relay conficuration, just bail nowtcontad key:"jiminny, coogie texthost') aa= nulb) {Snessacelds= sthis-srelayseryscesysync.orSthis-> uineld strna:Rellaved count scessacelds)" nessage (s)."Dcatch (fycention SelThsisesarron Sa.saer.assadeonSentou. cantiinesycentdalse*TO0У L7Thu 28 May 10:43:20coneolsereeconsolaisihework/src/Illuninate/View/MiddLeware/ShareErrorsFronSession.pho(48): 1 ₴V421Aework/src/Illuninate/Pipeline/PipeLine.php(219): Illuminate View Middleuarel|ShareE:rk/src/Illuninate/Session/Middleware/StartSession.php(120): Illuminatel|Pipelinellf/honc/jiminny/vendor/Laravel/francwork/src/Illuninatc/Session/MiddLeware/StartSession.php(63): Illuminatel|SessionllMid#33/honc/jiminny/vendor/Laravel/franework/src/T1luninatc/Pipcline/Pipcline.php(219): Illuninatel|Session||Middlcaarel|Star/honc/jiminny/app/Htto/Middleware/NoSessionForBots.pho(29): Tlluminatel\Pipelinel(Pipeline->fclosure:fclosure:T1luninatjiminny/vendon/laravel/franework/src/Tlluminate/Pipeline/Pipeline.php(219): Jiminny|(Httol|Middleuarel|NoSessionh/hone/jiminny/vendor/laravel/franework/src/Tlluminate/Cookie/Middleware/AddQueuedCookiesToResponse.pho (36): Tlluninate/hone/jiminny/vendor/laravel/franework/src/Illuninate/Pipeline/Pipeline.php(219): Illuninate\\Cookiel\MiddLeware||AddQyBeturinare PineiuineEli home/a5 m nny/vendod/adavs chako alowi cntoncoydes/soclle oudinontemyles, nhnvaarelumimare weine sne liine aineosialnndon nanaveFranework/sccartluminare/pinesine/oinesine.nholw insnovrnhdctenare1.71002m0yjor/Laravel/franework/src/Illuninate/Pipeline/Pipeline.php(219): Illuninate\\Http\\MiddLewarel\TrustP:avel//franework/scc/iiunsnate/Routna/Routen.nhn/8211lunsnatePrnelsneprnelna.sthennhwock/sce/Tlluefoate/Routkoo/Routen.nhn7ha mllunfnateRoutfndlRoutan.smuoDourtenhwock/sce/mlluefoste/Coundstfon/Wttn/Kennel.nhn/200. TllumfoataRout5oollPowtan.5052ework/src/Illuminate/Pipeline/Pipeline.php(180): IlluminateFoundation|Httpl|Kerneldb//aravalcdahuchar/cce/M6dalaans/Tofacthahuahan nhn/50%. Tllunioatal1050014o01105o014on.trk/src/Illuninate/Pipeline/PipeLine.php(219): BarryvdhDebugbarl|Middlcuarel Inieoork/src/1lluninatc/Http/Middleware/HandleCors.php(61): Illuminatel|PipeLinel|Pipelirrk/src/Tlluninate/PioeLine/PipeLine.php(219): Illuninatel\Htto||Middlcuarel|Handl.eurk/src/11Zuninate/Foundation/Htto/Middleware/TransformsReguest.pho(21): TlZuninatek/src/T1Zuninate/Foundation/Htto/Middleware/TrinStrings.oho(51): TlZuninatel|Foundrk/src/T1Zuninate/Pipeline/Pineline.ohp(219): Ylluninatel|FoundationllHttol|Middlerheaders/src/SecureHeadersMiddleware.oho(18): Ylluninatel\Pinelinel|Pineline->fclostork/src/Illuminate/Pipeline/Pipeline-php(219): Bepsvpt\|SecureHeaders||SecureHeadersvare/SentryContext.pho (69): Tlluninatel\Pipelinel\Pipeline->fclosure:fclosure:flluninatelork/src/f1luminate/PineLine/Pineline.pho(219): Jininnyl(Httol|Middleuarel |Sentcytontnework/src/Tlluninate/Foundatzion/Htto/Middleuare/PreventRequestsDuringMaintenance.phock/src/flluninate/PineLine/PineLine.pho(219): 111uninatel\Foundatsionl littollMiddeewock/src/11luninate/Foundatzion/Htto/Middleuare/InvokeDefercedCal1backs.oho (22)=111unanework/src/Illuninate/Pipeline/Pipeline.php(219): Illuninatel\Foundation|\Http|\MiddlerAbome/a5iminy/vendon AlacaveUaoamenockscclastumioatelcine Sinel2na bine ninekval etuminate toine snetpina simeotialnhone/jiminny/vendor/Laravel/franework/src/Illuninate/Foundation/Http/Kernel.php(17S): Illuminatel\Pipelinel\Pipeline-/bone/33m/onw//vendon/llanavel/franework/scc/T11unioate/Goundatfon/Htto/Kennell.nho/12/. Tl1uninatel|GouodatfoollHttollk#78 /hone/jiminny/pubLic/index.php(51): Illuminatel\Foundation| \₩ttp||Kernel->handLeCObJect(T1luminate| \Http|(Request))(connalation 4aw.10h702007-1272-[CREDIT_CARD]/752* H4AS0A 441.HHA260ARE-40S0-409d-0Kh4-55670570668h=)Fix. TextRelavServicecan I test it locally first. if I place env from staginsYes, The service relies on a tew thinas vou'd need in olace1. Credentials file The constructor expects storage/text-relay.ison (a Google service account JSON key file)Gmbtrom sthaind and oince it there2. .env values to copy from stagingGOOGLE TEXT USER=CANALE TEYT PELAY TOOTG# the Gnail mailbox address/e.o.tyt.ftotnaw.con.oftyt.cthafoo.fefonw.co# the Pub/Sub topic nameu or us3. Run the svnc manuallv vin tinker tinside docker 1aeo 10%mbasdocker exec ait docker laeo oho artfcan tiokeann/TininnvlCarvicaclMaf1NToytRolavGsricorseincelansmelhCaveat: The service reads from Gmail history and dispatches jobs to the queue. If you don't want it to actuallentorectly via tinker to verity the header loaic without disoatchino amvthinonuoh!Il Just test. the header readina logic on a known eessage 7hWiminny\ServicesVail\TextRelayService { public function constructlsom Serviceassvc-soctServicelcontiaminny.google textuser"KcAe theTiCAruSMNICAre BACCNAASWAATICAAEANEISIENAAN AAAAlA TAYt ICApI ITHE MESCAGG TOTNGadl -utemahienuao codiC AdaotivMactarcavlI Wiodsur Teams4R70-1UTE-яaudenod...
|
NULL
|
-7745877239697273072
|
NULL
|
click
|
ocr
|
NULL
|
rapstomViewNeweNNCCoocKetucioTOOI-WindowFV faVsco. rapstomViewNeweNNCCoocKetucioTOOI-WindowFV faVsco.|s ~$ JY-20915-fix-missing-header-text-rProinet vphe logos.phppheo matl.criyPmoxo.ongPYlkowie"medd"sueamine.oneHwowercalonephp queue.phpsdlestorce.onesamconephp secure-headers.phgwpservices.ohgppslack.ohephptimezones.ohgphp view.ohrphp webhook-server.oho> En contrit.→darabasrontcent>Ulangnode modues hhrary roon→tnanstant mualiid>@ resourcesv hroutesphe api.phpphe api_v2.phpne concale nbophe customer_api.phpMoamsioo0o.oneMo haalth obopupro cetco weo.oneMaweb chepp weonook.one> scriptev(ln storage> O apo>@ debugbar> &a frameworkviloasrcitionore0 audio wav= custom logcamnit xmFttiePTuthuARvatA ka3) SyncMailbox.php xockhrhttTextRelayServiceTest.phpclass SyncMailbox extends Command17 €246127 ФASA6KAKN8858*avan scrinaprorecred Sstonature = 'eartbox:text-relay: syng"?* The console comsand descriozon.* Evac stringprotected Sdescription = 'Sync text relay's mailbox with latest nessages.':2 usagesprivate Scelaysenv.ce;public function __construct(TextRelayService SrelayService)parent::_constructo:Sthis->relayService = SrelayService:icxccure ch consore comnaheouoeeTunceoh ninete voretoy «iI/ If we don't have relay conficuration, just bail nowtcontad key:"jiminny, coogie texthost') aa= nulb) {Snessacelds= sthis-srelayseryscesysync.orSthis-> uineld strna:Rellaved count scessacelds)" nessage (s)."Dcatch (fycention SelThsisesarron Sa.saer.assadeonSentou. cantiinesycentdalse*TO0У L7Thu 28 May 10:43:20coneolsereeconsolaisihework/src/Illuninate/View/MiddLeware/ShareErrorsFronSession.pho(48): 1 ₴V421Aework/src/Illuninate/Pipeline/PipeLine.php(219): Illuminate View Middleuarel|ShareE:rk/src/Illuninate/Session/Middleware/StartSession.php(120): Illuminatel|Pipelinellf/honc/jiminny/vendor/Laravel/francwork/src/Illuninatc/Session/MiddLeware/StartSession.php(63): Illuminatel|SessionllMid#33/honc/jiminny/vendor/Laravel/franework/src/T1luninatc/Pipcline/Pipcline.php(219): Illuninatel|Session||Middlcaarel|Star/honc/jiminny/app/Htto/Middleware/NoSessionForBots.pho(29): Tlluminatel\Pipelinel(Pipeline->fclosure:fclosure:T1luninatjiminny/vendon/laravel/franework/src/Tlluminate/Pipeline/Pipeline.php(219): Jiminny|(Httol|Middleuarel|NoSessionh/hone/jiminny/vendor/laravel/franework/src/Tlluminate/Cookie/Middleware/AddQueuedCookiesToResponse.pho (36): Tlluninate/hone/jiminny/vendor/laravel/franework/src/Illuninate/Pipeline/Pipeline.php(219): Illuninate\\Cookiel\MiddLeware||AddQyBeturinare PineiuineEli home/a5 m nny/vendod/adavs chako alowi cntoncoydes/soclle oudinontemyles, nhnvaarelumimare weine sne liine aineosialnndon nanaveFranework/sccartluminare/pinesine/oinesine.nholw insnovrnhdctenare1.71002m0yjor/Laravel/franework/src/Illuninate/Pipeline/Pipeline.php(219): Illuninate\\Http\\MiddLewarel\TrustP:avel//franework/scc/iiunsnate/Routna/Routen.nhn/8211lunsnatePrnelsneprnelna.sthennhwock/sce/Tlluefoate/Routkoo/Routen.nhn7ha mllunfnateRoutfndlRoutan.smuoDourtenhwock/sce/mlluefoste/Coundstfon/Wttn/Kennel.nhn/200. TllumfoataRout5oollPowtan.5052ework/src/Illuminate/Pipeline/Pipeline.php(180): IlluminateFoundation|Httpl|Kerneldb//aravalcdahuchar/cce/M6dalaans/Tofacthahuahan nhn/50%. Tllunioatal1050014o01105o014on.trk/src/Illuninate/Pipeline/PipeLine.php(219): BarryvdhDebugbarl|Middlcuarel Inieoork/src/1lluninatc/Http/Middleware/HandleCors.php(61): Illuminatel|PipeLinel|Pipelirrk/src/Tlluninate/PioeLine/PipeLine.php(219): Illuninatel\Htto||Middlcuarel|Handl.eurk/src/11Zuninate/Foundation/Htto/Middleware/TransformsReguest.pho(21): TlZuninatek/src/T1Zuninate/Foundation/Htto/Middleware/TrinStrings.oho(51): TlZuninatel|Foundrk/src/T1Zuninate/Pipeline/Pineline.ohp(219): Ylluninatel|FoundationllHttol|Middlerheaders/src/SecureHeadersMiddleware.oho(18): Ylluninatel\Pinelinel|Pineline->fclostork/src/Illuminate/Pipeline/Pipeline-php(219): Bepsvpt\|SecureHeaders||SecureHeadersvare/SentryContext.pho (69): Tlluninatel\Pipelinel\Pipeline->fclosure:fclosure:flluninatelork/src/f1luminate/PineLine/Pineline.pho(219): Jininnyl(Httol|Middleuarel |Sentcytontnework/src/Tlluninate/Foundatzion/Htto/Middleuare/PreventRequestsDuringMaintenance.phock/src/flluninate/PineLine/PineLine.pho(219): 111uninatel\Foundatsionl littollMiddeewock/src/11luninate/Foundatzion/Htto/Middleuare/InvokeDefercedCal1backs.oho (22)=111unanework/src/Illuninate/Pipeline/Pipeline.php(219): Illuninatel\Foundation|\Http|\MiddlerAbome/a5iminy/vendon AlacaveUaoamenockscclastumioatelcine Sinel2na bine ninekval etuminate toine snetpina simeotialnhone/jiminny/vendor/Laravel/franework/src/Illuninate/Foundation/Http/Kernel.php(17S): Illuminatel\Pipelinel\Pipeline-/bone/33m/onw//vendon/llanavel/franework/scc/T11unioate/Goundatfon/Htto/Kennell.nho/12/. Tl1uninatel|GouodatfoollHttollk#78 /hone/jiminny/pubLic/index.php(51): Illuminatel\Foundation| \₩ttp||Kernel->handLeCObJect(T1luminate| \Http|(Request))(connalation 4aw.10h702007-1272-[CREDIT_CARD]/752* H4AS0A 441.HHA260ARE-40S0-409d-0Kh4-55670570668h=)Fix. TextRelavServicecan I test it locally first. if I place env from staginsYes, The service relies on a tew thinas vou'd need in olace1. Credentials file The constructor expects storage/text-relay.ison (a Google service account JSON key file)Gmbtrom sthaind and oince it there2. .env values to copy from stagingGOOGLE TEXT USER=CANALE TEYT PELAY TOOTG# the Gnail mailbox address/e.o.tyt.ftotnaw.con.oftyt.cthafoo.fefonw.co# the Pub/Sub topic nameu or us3. Run the svnc manuallv vin tinker tinside docker 1aeo 10%mbasdocker exec ait docker laeo oho artfcan tiokeann/TininnvlCarvicaclMaf1NToytRolavGsricorseincelansmelhCaveat: The service reads from Gmail history and dispatches jobs to the queue. If you don't want it to actuallentorectly via tinker to verity the header loaic without disoatchino amvthinonuoh!Il Just test. the header readina logic on a known eessage 7hWiminny\ServicesVail\TextRelayService { public function constructlsom Serviceassvc-soctServicelcontiaminny.google textuser"KcAe theTiCAruSMNICAre BACCNAASWAATICAAEANEISIENAAN AAAAlA TAYt ICApI ITHE MESCAGG TOTNGadl -utemahienuao codiC AdaotivMactarcavlI Wiodsur Teams4R70-1UTE-яaudenod...
|
80852
|
NULL
|
NULL
|
NULL
|
|
80915
|
2814
|
33
|
2026-05-28T07:45:33.088249+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779954333088_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20915-fix-missing-header-text-relay, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20915-fix-missing-header-text-relay","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TextRelayServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TextRelayServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
4913519257762175176
|
2897453187665953201
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20915-fix-missing-head Project: faVsco.js, menu
JY-20915-fix-missing-header-text-relay, menu
Start Listening for PHP Debug Connections
TextRelayServiceTest
Run 'TextRelayServiceTest'
Debug 'TextRelayServiceTest'
iTerm2ShellEditViewSessionScriptsProfilesWindowHelp100% C8• Thu 28 May 10:45:32• 0DEV (docker)screenpipe"181DOCKER₴81DEV (docker)₴2-zsh88384-zshX5ec2-user@ip-10-30-1...O ₴6ec2-user@ip-10-30-140-...$7lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny~/Jiminny/app JY-20915-fix-missing-header-text-relay) $ co master. env.localapp/Console/Commands/JiminnyDebugCommand.phpapp/Services/Mail/TextRelayService.phpconfig/logging.phpSwitched to branch 'master'Your branch is up to date with 'origin/master'.DEVlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (master) $ git pullremote: Enumerating objects: 250, done.remote: Countingobjects: 100% (198/198), done.remote: Compressing objects: 100% (70/70), done.remote: Total 250 (delta 154), reused 138 (delta128), pack-reused 52 (from 2)Receiving objects: 100% (250/250), 99.48 KiB | 783.00 KiB/s,done.Resolving deltas: 100% (160/160), completed with 46 localobjects.From github.com:jiminny/app890e429206..828865edebmastera867cc532f..c9701224df-> origin/masterJY-20664-customer-api-exec-reports-> origin/JY-20664-customer-api-exec-reports+ 8ee439f68c...145d59435f JY-20828-user-can-manage-desktop-recording-permissions-> origin/JY-20828-user-can-manage-desktop-recording-permissions(forced update)8564b059d7..6ef3afd1d0JY-20835-mcp-enable-users-to-get-a-list-of-deals-and-their-details -> origin/JY-20835-mcp-enable-users-to-get-a-list-of-deals-and-their-details* [2ew brane. 6C3bC795af JY-20855-mcp-Es-mappings-> origin/JY-20855-mcp-ES-mappings (forced update)JY-20855-mcp-ES-mappings-BCKP-before-rebase-> origin/JY-20855-mcp-ES-mappings-BCKP-before-rebaseb3659b1290. .eb199eee57JY-20910-schedule-parallel-update-target-processing-> origin/JY-20910-schedule-parallel-update-target-processing8afc20af84..9e9a0f0bd4claude-reviewer-improvements-4-> origin/claude-reviewer-improvements-4* [new branch]master-deploy-> origin/master-deployUpdating 890e429206. .828865edebFast-forwarddatabase/mappings/mapping_activities.jsondatabase/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.php || 341312 files changed, 372 insertions(+)create mode 100644 database/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.phplukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (master) $ coJY-20915-fix-missing-header-text-relay. env.localMapp/Console/Commands/JiminnyDebugCommand.phpapp/Services/Mail/TextRelayService.phpconfig/logging.phpSwitched to branch 'JY-20915-fix-missing-header-text-relay'lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20915-fix-missing-header-text-relay) $ git merge masterUpdating 890e429206..828865edebFast-forwarddatabase/mappings/mapping_activities.jsondatabase/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.php l+++2 files changed, 372 insertions(+)create mode 100644 database/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.phplukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20915-fix-missing-header-text-relay) $ devroot@docker_lamp_1:/home/jiminny# ]...
|
80912
|
NULL
|
NULL
|
NULL
|
|
80916
|
2815
|
40
|
2026-05-28T07:45:36.249150+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779954336249_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
rapstomViewNeweNNCCoocKetucioTOOI-WindowFV faVsco. rapstomViewNeweNNCCoocKetucioTOOI-WindowFV faVsco.|s ~$ JY-20915-fix-missing-header-text-rProinet v3) SyncMailbox.php xphe logos.phppheo matl.criyPmaxio.ong"medd"sueamine.oneHwowercalonephp queue.phpsdlestorce.onesamconephp secure-headers.phgwpservices.ohoppslack.ohephptimezones.ohgphp view.ohrphp webhook-server.oho> En contrit.→darabasrontcent>Elangnode modtles hhrary toon→tnanstant mualiid>@ resourcesv hroutesphe api.phpphe api_v2.phpne concole nbophe customer_api.phpMoamsioo0o.oneMo haalth obomuoro celcoweo.onMaweb chepp weonook.one> scriptev(ln storage> O apo>@ debugbar> &a frameworkvloascitianore0 audio wav= custom ogE hubspot-lournal-pollllocockhrhttTextRelayServiceTest.phpclass SyncMailbox extends Command17 €246127 ФKAK8&S8=aravelcacamnit xmEoauthenrivate kan*avan scrinaprorecred Sstonature = 'eartbox:text-relay: syng"?* The console comsand descriozon.* Evac stringprotected Sdescription = 'Sync text relay's mailbox with latest nessages.':2 usagesprivate Scelaysenv.ce;public function __construct(TextRelayService SrelayService,parent::_constructo:Sthis->relayService = SrelayService:icxccure ch consore comnahepublic function handled: voidtoy «iI/ If we don't have relay conficuration, just bail nowtcontad key:"jiminny,coogie texthost') aa= nulb) {Snessacelds= sthis->relayseryscesysynciorSthis-›Line( strina: 'Relaved * count(S-essagelds). * nessage(s). '):catch (Fycention SelhsisaserronSa.saarlascade.onSentou. cantiinesycentdalse*SF (iminny@localhost)HS.Jocal jiminny@blocalhost)& console [PROD# console leulA console (STAGING) X2826-85-28 87:41:58 Local.INF0: SparanshistoryTypes = messageAddedstartHistoryId) => 359921id*:*228678ba-4380-41be-9a88-62919d6a1690**(2826-85-28 87:41:591 Local.INF0: SnessageHistorvcorrelarion.1d:03y/80855-2005-4ad4-85co-d8eao5co97errace-10: [CREDIT_CARD]-[CREDIT_CARD]-418h6-85o8 8rruseelomieuftsharaneIhistorylypes => messageAddecstartHistorytoll50%21d":"d188abdc-755f-4f98-862a-d28ce8fa8c7d*)2826-85-28 87:45:201 1ocl-TNED: Snessadels stomuEconnelatton Sdw.*a04540s7-8/42-/537-6757-61210/016/109 "tлACA K/R.9418Rahdc.7554-1400-8629-d28ce8faAc74")TO0%7A• Thu 28 May 10:45:36TextRelayServiceTestaner hensewiet+0.can I test it locally first. if I place env from stagingYes. The service relies on a tew thinas vou'd need in place1. Credentials file The constructor expects storage/text-relay.ison (a Google service account JSON key file)Gmbtrom sthaind and oince it there2. .env values to copy from stagingGOOGLE TEXTUSERECAOGLE TEXT HASTGOOGLE TEXT RELAY TOPIC:DEPLOY REGION:the cnail mailbox address# e.g. txt.jiminny.com or txt.staging.jiminny.comaeu or us3. Run the svnc manuallv vin tinker tinside docker 1aeo 10%b basaegdocker exec -it docker_lamp_1 php artisan tinkeapp(\Jiminny\Services\Ma1l\TextRelayService::class)-sync():Caveat: The service reads from Gmail history and dispatches jobs to the queue. If you don't want it to actuallsend SMS, make sure QUEUE_CONNECTICN»sync is not set (or use a test queue), or just call isForCurrentEnvironntioh.Ssvc = new class extends \Jiminny\Services\Mail\TextRelayService & public function _construct(Som Service ssvc-soctServicelcont.aeainI then sinsoect a eessade canual1sasg= sonailService-susers_messages-sget(config(*iminty.google text user'), *THE_MESSAGE ID')collect(5-s0-20etPayload()-soetWeaders())-soluck("value"thut wey von can Mrity what handare tra nehhilm oraesot do ttat macesos alote tha ull evoe muneAsk anything (XOL)-@ eodeAdhotvwodeurtate*4 space...
|
NULL
|
-86133116693620493
|
NULL
|
click
|
ocr
|
NULL
|
rapstomViewNeweNNCCoocKetucioTOOI-WindowFV faVsco. rapstomViewNeweNNCCoocKetucioTOOI-WindowFV faVsco.|s ~$ JY-20915-fix-missing-header-text-rProinet v3) SyncMailbox.php xphe logos.phppheo matl.criyPmaxio.ong"medd"sueamine.oneHwowercalonephp queue.phpsdlestorce.onesamconephp secure-headers.phgwpservices.ohoppslack.ohephptimezones.ohgphp view.ohrphp webhook-server.oho> En contrit.→darabasrontcent>Elangnode modtles hhrary toon→tnanstant mualiid>@ resourcesv hroutesphe api.phpphe api_v2.phpne concole nbophe customer_api.phpMoamsioo0o.oneMo haalth obomuoro celcoweo.onMaweb chepp weonook.one> scriptev(ln storage> O apo>@ debugbar> &a frameworkvloascitianore0 audio wav= custom ogE hubspot-lournal-pollllocockhrhttTextRelayServiceTest.phpclass SyncMailbox extends Command17 €246127 ФKAK8&S8=aravelcacamnit xmEoauthenrivate kan*avan scrinaprorecred Sstonature = 'eartbox:text-relay: syng"?* The console comsand descriozon.* Evac stringprotected Sdescription = 'Sync text relay's mailbox with latest nessages.':2 usagesprivate Scelaysenv.ce;public function __construct(TextRelayService SrelayService,parent::_constructo:Sthis->relayService = SrelayService:icxccure ch consore comnahepublic function handled: voidtoy «iI/ If we don't have relay conficuration, just bail nowtcontad key:"jiminny,coogie texthost') aa= nulb) {Snessacelds= sthis->relayseryscesysynciorSthis-›Line( strina: 'Relaved * count(S-essagelds). * nessage(s). '):catch (Fycention SelhsisaserronSa.saarlascade.onSentou. cantiinesycentdalse*SF (iminny@localhost)HS.Jocal jiminny@blocalhost)& console [PROD# console leulA console (STAGING) X2826-85-28 87:41:58 Local.INF0: SparanshistoryTypes = messageAddedstartHistoryId) => 359921id*:*228678ba-4380-41be-9a88-62919d6a1690**(2826-85-28 87:41:591 Local.INF0: SnessageHistorvcorrelarion.1d:03y/80855-2005-4ad4-85co-d8eao5co97errace-10: [CREDIT_CARD]-[CREDIT_CARD]-418h6-85o8 8rruseelomieuftsharaneIhistorylypes => messageAddecstartHistorytoll50%21d":"d188abdc-755f-4f98-862a-d28ce8fa8c7d*)2826-85-28 87:45:201 1ocl-TNED: Snessadels stomuEconnelatton Sdw.*a04540s7-8/42-/537-6757-61210/016/109 "tлACA K/R.9418Rahdc.7554-1400-8629-d28ce8faAc74")TO0%7A• Thu 28 May 10:45:36TextRelayServiceTestaner hensewiet+0.can I test it locally first. if I place env from stagingYes. The service relies on a tew thinas vou'd need in place1. Credentials file The constructor expects storage/text-relay.ison (a Google service account JSON key file)Gmbtrom sthaind and oince it there2. .env values to copy from stagingGOOGLE TEXTUSERECAOGLE TEXT HASTGOOGLE TEXT RELAY TOPIC:DEPLOY REGION:the cnail mailbox address# e.g. txt.jiminny.com or txt.staging.jiminny.comaeu or us3. Run the svnc manuallv vin tinker tinside docker 1aeo 10%b basaegdocker exec -it docker_lamp_1 php artisan tinkeapp(\Jiminny\Services\Ma1l\TextRelayService::class)-sync():Caveat: The service reads from Gmail history and dispatches jobs to the queue. If you don't want it to actuallsend SMS, make sure QUEUE_CONNECTICN»sync is not set (or use a test queue), or just call isForCurrentEnvironntioh.Ssvc = new class extends \Jiminny\Services\Mail\TextRelayService & public function _construct(Som Service ssvc-soctServicelcont.aeainI then sinsoect a eessade canual1sasg= sonailService-susers_messages-sget(config(*iminty.google text user'), *THE_MESSAGE ID')collect(5-s0-20etPayload()-soetWeaders())-soluck("value"thut wey von can Mrity what handare tra nehhilm oraesot do ttat macesos alote tha ull evoe muneAsk anything (XOL)-@ eodeAdhotvwodeurtate*4 space...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
80917
|
2814
|
34
|
2026-05-28T07:45:36.354296+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-28/1779 /Users/lukas/.screenpipe/data/data/2026-05-28/1779954336354_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp<100% C8• Thu 28 May 10:45:36• 0DEV (docker)1881DOCKER- ₴81DEV (docker)₴2-zsh883ffmpeg0 84-zshX5ec2-user@ip-10-30-1...O 86ec2-user@ip-10-30-140-…..$7lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny~/Jiminny/app JY-20915-fix-missing-header-text-relay) $ co master. env.localapp/Console/Commands/JiminnyDebugCommand.phpapp/Services/Mail/TextRelayService.phpconfig/logging.phpSwitched to branch 'master'Your branch is up to date with 'origin/master'.DEVlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (master) $ git pullremote: Enumerating objects: 250, done.remote: Countingobjects: 100% (198/198), done.remote: Compressing objects: 100% (70/70), done.remote: Total 250 (delta 154), reused 138 (delta128), pack-reused 52 (from 2)Receiving objects: 100% (250/250), 99.48 KiB | 783.00 KiB/s,done.Resolving deltas: 100% (160/160), completed with 46 localobjects.From github.com: jiminny/app890e429206..828865edebmastera867cc532f..c9701224df-> origin/masterJY-20664-customer-api-exec-reports-> origin/JY-20664-customer-api-exec-reports+ 8ee439f68c...145d59435f JY-20828-user-can-manage-desktop-recording-permissions-> origin/JY-20828-user-can-manage-desktop-recording-permissions(forced update)8564b059d7..6ef3afd1d0JY-20835-mcp-enable-users-to-get-a-list-of-deals-and-their-details -> origin/JY-20835-mcp-enable-users-to-get-a-list-of-deals-and-their-details+ 12be38addb.. .6c8bc795df JY-20855-mcp-ES-mappinas-> origin/JY-20855-mcp-ES-mappings (forced update)* Inew branch1JY-20855-mcp-ES-mappings-BCKP-before-rebase-> origin/JY-20855-mcp-ES-mappings-BCKP-before-rebaseb3659b1290. .eb199eee57JY-20910-schedule-parallel-update-target-processing-> origin/JY-20910-schedule-parallel-update-target-processing8afc20af84..9e9a0f0bd4claude-reviewer-improvements-4-> origin/claude-reviewer-improvements-4* [new branch]master-deploy-> origin/master-deployUpdating 890e429206. .828865edebFast-forwarddatabase/mappings/mapping_activities.jsondatabase/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.php || 341312 files changed, 372 insertions(+)create mode 100644 database/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.phplukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (master) $ coJY-20915-fix-missing-header-text-relay. env.localMapp/Console/Commands/JiminnyDebugCommand.phpapp/Services/Mail/TextRelayService.phpconfig/logging.phpSwitched to branch 'JY-20915-fix-missing-header-text-relay'lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20915-fix-missing-header-text-relay) $ git merge masterUpdating 890e429206..828865edebFast-forwarddatabase/mappings/mapping_activities.jsondatabase/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.php l+++2 files changed, 372 insertions(+)create mode 100644 database/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.phplukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20915-fix-missing-header-text-relay) $ devroot@docker_lamp_1:/home/jiminny# ]...
|
NULL
|
1276657870850928183
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp<100% C8• Thu 28 May 10:45:36• 0DEV (docker)1881DOCKER- ₴81DEV (docker)₴2-zsh883ffmpeg0 84-zshX5ec2-user@ip-10-30-1...O 86ec2-user@ip-10-30-140-…..$7lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny~/Jiminny/app JY-20915-fix-missing-header-text-relay) $ co master. env.localapp/Console/Commands/JiminnyDebugCommand.phpapp/Services/Mail/TextRelayService.phpconfig/logging.phpSwitched to branch 'master'Your branch is up to date with 'origin/master'.DEVlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (master) $ git pullremote: Enumerating objects: 250, done.remote: Countingobjects: 100% (198/198), done.remote: Compressing objects: 100% (70/70), done.remote: Total 250 (delta 154), reused 138 (delta128), pack-reused 52 (from 2)Receiving objects: 100% (250/250), 99.48 KiB | 783.00 KiB/s,done.Resolving deltas: 100% (160/160), completed with 46 localobjects.From github.com: jiminny/app890e429206..828865edebmastera867cc532f..c9701224df-> origin/masterJY-20664-customer-api-exec-reports-> origin/JY-20664-customer-api-exec-reports+ 8ee439f68c...145d59435f JY-20828-user-can-manage-desktop-recording-permissions-> origin/JY-20828-user-can-manage-desktop-recording-permissions(forced update)8564b059d7..6ef3afd1d0JY-20835-mcp-enable-users-to-get-a-list-of-deals-and-their-details -> origin/JY-20835-mcp-enable-users-to-get-a-list-of-deals-and-their-details+ 12be38addb.. .6c8bc795df JY-20855-mcp-ES-mappinas-> origin/JY-20855-mcp-ES-mappings (forced update)* Inew branch1JY-20855-mcp-ES-mappings-BCKP-before-rebase-> origin/JY-20855-mcp-ES-mappings-BCKP-before-rebaseb3659b1290. .eb199eee57JY-20910-schedule-parallel-update-target-processing-> origin/JY-20910-schedule-parallel-update-target-processing8afc20af84..9e9a0f0bd4claude-reviewer-improvements-4-> origin/claude-reviewer-improvements-4* [new branch]master-deploy-> origin/master-deployUpdating 890e429206. .828865edebFast-forwarddatabase/mappings/mapping_activities.jsondatabase/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.php || 341312 files changed, 372 insertions(+)create mode 100644 database/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.phplukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (master) $ coJY-20915-fix-missing-header-text-relay. env.localMapp/Console/Commands/JiminnyDebugCommand.phpapp/Services/Mail/TextRelayService.phpconfig/logging.phpSwitched to branch 'JY-20915-fix-missing-header-text-relay'lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20915-fix-missing-header-text-relay) $ git merge masterUpdating 890e429206..828865edebFast-forwarddatabase/mappings/mapping_activities.jsondatabase/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.php l+++2 files changed, 372 insertions(+)create mode 100644 database/migrations/2026_05_27_000000_add_ai_call_scores_to_automated_reports_table.phplukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20915-fix-missing-header-text-relay) $ devroot@docker_lamp_1:/home/jiminny# ]...
|
NULL
|
NULL
|
NULL
|
NULL
|