|
78774
|
2763
|
49
|
2026-05-27T12:59:32.825098+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886772825_m1.jpg...
|
PhpStorm
|
faVsco.js – SyncMailbox.php
|
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 12:58:55] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
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":true,"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 12:58:55] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}","depth":4,"on_screen":true,"value":"[2026-05-27 12:58:55] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"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}]...
|
-7411521242760274630
|
-1317635867829649074
|
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 12:58:55] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78769
|
NULL
|
NULL
|
NULL
|
|
78773
|
2764
|
55
|
2026-05-27T12:59:26.955226+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886766955_m2.jpg...
|
PhpStorm
|
faVsco.js – SyncMailbox.php
|
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...
|
[{"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.0622506,"width":0.27227393,"height":0.9377494},"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":true,"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}]...
|
9059154762099916582
|
-1317651261797465786
|
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...
|
78772
|
NULL
|
NULL
|
NULL
|
|
78772
|
2764
|
54
|
2026-05-27T12:59:15.093402+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886755093_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
[2026-05-27 12:58:55] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
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":true,"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":"[2026-05-27 12:58:55] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}","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":51,"bounds":{"left":0.43051863,"top":0.0,"width":0.12965426,"height":0.014365523}},{"char_start":280,"char_count":6,"bounds":{"left":0.43051863,"top":0.0,"width":0.012965426,"height":0.014365523}},{"char_start":286,"char_count":2,"bounds":{"left":0.43051863,"top":0.0,"width":0.0026595744,"height":0.014365523}},{"char_start":288,"char_count":2,"bounds":{"left":0.43051863,"top":0.0,"width":0.0026595744,"height":0.014365523}},{"char_start":290,"char_count":109,"bounds":{"left":0.43051863,"top":0.008778931,"width":0.28257978,"height":0.014365523}}],"value":"[2026-05-27 12:58:55] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"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}]...
|
2035070021299149985
|
-6434375025245647989
|
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
[2026-05-27 12:58:55] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78771
|
2764
|
53
|
2026-05-27T12:59:10.357594+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886750357_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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...
|
[{"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}]...
|
-4060176888399009113
|
-9212323791225028208
|
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
PhpStormViewNeWENNCCoocRefactoRurToolWindowFV faVsco.s$ JY-20915-fix-missing-header-text-relaproidet(C) Kernelphp© SyncMailbox.phpDockerfilephe logos.phopp malionoppmaxio.oneppmep.onoprp media-steamino.onophe prophet-al.phpppqueue.onppte salesforce.phgppsamiz.oneweeetnoonopho view.ohopho webhook-sarver.ono>e contnt>IM database>e doce>e tronteend>Blange node modules library root>BphpstanSemihiaS IMrocmirnochvimrmitosphp api.phpphp api-v2.phpMo cansole,onopp customer apronpphe embedded.phpwPneaith.ohpww scim.ohpwpupro.ectedweb.ongppweb.ohoppwebhook.ono>Mscriotey Mstomad>Uapp→ demuanr>@ frameworkcitionoreolaudio warTextRelayServiceTest.phpphp fiminny.phg3.env.productionclass TextRelayService= A1A1 A VНННННРНВВ 5 с 88public function getHistory(Google6nail Sservice): array= hubspot-journal-pollllog= laravel.logchounit.xmSpageToken = null;Stopic = confiq@key: "jininny.google.text_relay topic'):oaessaces-Shistoryld=cachoceoooI f we have no history stored, WatchMailboxEvents must not have run yet :/if (Shistoryid == false) "Shistoryid = Sthis->refreshHistoryPoint(Stonic):Soarans=starthstorud a> shistoryiodotry ?if (SpageToken) {Sparansl'pageToken") = SpageToken:lLuninate (Support (Facades (Log::channel( channel: "custon_channel")->info("Sparans" . PHP_LShistoryResponse = Sservice->users_history->ListUsersHistory(config(key."jininny.google_text_user')ConmandSthis-›setHistoryPoint(Stopic, (int) ShistoryResponse->historyId):sf (ShistoryResponse->getHistory0)Snessages = array-nerge(Snessages. ShistoryResponse-›getHistoryOD:oaoelokene snstorkesconsoooeexracelokenies} catch (Exceptiion Se) fLoosserror( messaco: "[TextRelavServicel Failed to fetch Gmail histony'. "'excentiion' => Se-sgetMessagedSentoy: canture sxcenttonise"whiile (SpageToken):retuon Snessades,nrotectedfunctsion sechiiscoryposinestring Stogic, int Shistoryeoiint Cacbont..hia tishMecl vetite oot wdelohta Moul caneminutas taoySF (iminny@localhost)HSJocal jiminny@blocalhost)A console (STAGING2826-85-27 12:58:55 Local.INF0: SparanshistoryTypes = messageAddedstartHistoryId) => 358991correlazon10:acc/0809-6087-4c9c-8ac8-669a318508577826-85-27 7:58:55 Local.wru: Snessagchzscory# console (PRont# console leul{d":"b2519cfa-d38f-42c4-899b-32c435d3bf8c*}yrace.10: [CREDIT_CARD]-[CREDIT_CARD]-5300%L2*Wed 27 May 15:59:10TextRelayServiceTestner 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 TEXT HASTGOOGLE TEXT RELAY TOPICDEPLOY REGION:the cnail mailbox 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 au eessade canuai. 1sasg= sonailService-susers_messages-sget(config(*iminty.google text user'), *THE_MESSAGE ID')collect(5-s0-20etPayload()-soetWeaders())-soluck("value"thut wey von can Mrit whht nandore tra nenhily oraeaet do htas macesod Calora tha ull evoc nineAsk anything (XoL)- @ eodeAdhotvwodeurtate*4 space...
|
78770
|
NULL
|
NULL
|
NULL
|
|
78770
|
2764
|
52
|
2026-05-27T12:59:02.825250+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886742825_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
[2026-05-27 12:58:55] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
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":"[2026-05-27 12:58:55] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}","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":51,"bounds":{"left":0.43051863,"top":0.0,"width":0.12965426,"height":0.014365523}},{"char_start":280,"char_count":6,"bounds":{"left":0.43051863,"top":0.0,"width":0.012965426,"height":0.014365523}},{"char_start":286,"char_count":2,"bounds":{"left":0.43051863,"top":0.0,"width":0.0026595744,"height":0.014365523}},{"char_start":288,"char_count":2,"bounds":{"left":0.43051863,"top":0.0,"width":0.0026595744,"height":0.014365523}},{"char_start":290,"char_count":109,"bounds":{"left":0.43051863,"top":0.008778931,"width":0.28257978,"height":0.014365523}}],"value":"[2026-05-27 12:58:55] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}","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}]...
|
2035070021299149985
|
-6434375025245647989
|
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
[2026-05-27 12:58:55] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78769
|
2763
|
48
|
2026-05-27T12:59:01.174578+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886741174_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
[2026-05-27 12:58:55] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
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":"[2026-05-27 12:58:55] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}","depth":4,"on_screen":true,"value":"[2026-05-27 12:58:55] local.INFO: $params \nArray\n(\n [historyTypes] => messageAdded\n [startHistoryId] => 358991\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}","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}]...
|
2035070021299149985
|
-6434375025245647989
|
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
[2026-05-27 12:58:55] local.INFO: $params
Array
(
[historyTypes] => messageAdded
[startHistoryId] => 358991
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78768
|
2763
|
47
|
2026-05-27T12:59:00.182659+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886740182_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.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
3
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 12:58:51] local.INFO: [Commands/AsyncUpdateEsEntities] Starting ES update worker {"pid":50462,"target":"activities"} {"correlation_id":"03c42a91-960a-44ad-bc97-adf7763ecc24","trace_id":"07717139-829a-4708-8b68-9942c450fe91"}
[2026-05-27 12:58:54] 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":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:54] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] 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":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
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":"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":"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":"[2026-05-27 12:58:51] local.INFO: [Commands/AsyncUpdateEsEntities] Starting ES update worker {\"pid\":50462,\"target\":\"activities\"} {\"correlation_id\":\"03c42a91-960a-44ad-bc97-adf7763ecc24\",\"trace_id\":\"07717139-829a-4708-8b68-9942c450fe91\"}\n[2026-05-27 12:58:54] 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\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:54] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] 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\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}","depth":4,"on_screen":true,"value":"[2026-05-27 12:58:51] local.INFO: [Commands/AsyncUpdateEsEntities] Starting ES update worker {\"pid\":50462,\"target\":\"activities\"} {\"correlation_id\":\"03c42a91-960a-44ad-bc97-adf7763ecc24\",\"trace_id\":\"07717139-829a-4708-8b68-9942c450fe91\"}\n[2026-05-27 12:58:54] 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\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:54] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}\n[2026-05-27 12:58:55] 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\":\"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31\",\"trace_id\":\"b2519efa-d38f-42c4-899b-32c435d3bf8e\"}","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}]...
|
-4588232643638559979
|
-6434436516300813941
|
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;
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
3
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 12:58:51] local.INFO: [Commands/AsyncUpdateEsEntities] Starting ES update worker {"pid":50462,"target":"activities"} {"correlation_id":"03c42a91-960a-44ad-bc97-adf7763ecc24","trace_id":"07717139-829a-4708-8b68-9942c450fe91"}
[2026-05-27 12:58:54] 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":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:54] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
[2026-05-27 12:58:55] 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":"acc2b8e9-6d87-4c9e-8ae0-6c9aa105ba31","trace_id":"b2519efa-d38f-42c4-899b-32c435d3bf8e"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78767
|
NULL
|
NULL
|
NULL
|
|
78767
|
2763
|
46
|
2026-05-27T12:58:58.061994+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886738061_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.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;
}
}...
|
[{"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}]...
|
2618200518122746620
|
-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;
}
}...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78764
|
2764
|
50
|
2026-05-27T12:58:46.784169+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886726784_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.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;
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
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":"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":"AXTextArea","text":"","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"value":"","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}]...
|
5130192760835644086
|
-6434374888886727797
|
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;
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78763
|
2764
|
49
|
2026-05-27T12:58:46.204800+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886726204_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.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;
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
10
Previous Highlighted Error
Next Highlighted Error
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":"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":"10","depth":4,"bounds":{"left":0.72772604,"top":0.10055866,"width":0.009640957,"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":"","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"value":"","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}]...
|
3979950759907404324
|
-6434375026325943413
|
typing_pause
|
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
10
Previous Highlighted Error
Next Highlighted Error
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78761
|
NULL
|
NULL
|
NULL
|
|
78762
|
2763
|
44
|
2026-05-27T12:58:44.172157+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886724172_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.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
10
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 12:57:12] local.NOTICE: Monitoring start {"correlation_id":"01b85e02-17ff-44f0-8d6e-484a810b1085","trace_id":"b4d68a19-67e7-41f9-a2f3-ca98f84e617b"}
[2026-05-27 12:57:12] local.NOTICE: Monitoring end {"correlation_id":"01b85e02-17ff-44f0-8d6e-484a810b1085","trace_id":"b4d68a19-67e7-41f9-a2f3-ca98f84e617b"}
[2026-05-27 12:57: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":"829043ea-15e1-40fe-a7ce-27fc8440984e","trace_id":"bd9f99cc-933a-453b-b9ff-869d04e9b49d"}
[2026-05-27 12:57: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":"829043ea-15e1-40fe-a7ce-27fc8440984e","trace_id":"bd9f99cc-933a-453b-b9ff-869d04e9b49d"}
[2026-05-27 12:57: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":"eb849911-4661-4753-b85d-3bd9dd3b0b9d","trace_id":"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a"}
[2026-05-27 12:57:15] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"eb849911-4661-4753-b85d-3bd9dd3b0b9d","trace_id":"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a"}
[2026-05-27 12:57:15] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"eb849911-4661-4753-b85d-3bd9dd3b0b9d","trace_id":"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a"}
[2026-05-27 12:57: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":"eb849911-4661-4753-b85d-3bd9dd3b0b9d","trace_id":"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a"}
[2026-05-27 12:57:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:create","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"1743fc5e-a278-45c5-989d-06afb0bfe24e","trace_id":"872b746a-3e40-49a0-b858-c4265fb9ebf1"}
[2026-05-27 12:57:19] local.INFO: [EmailSchedule] STARTING batch create {"host":"docker_lamp_1"} {"correlation_id":"1743fc5e-a278-45c5-989d-06afb0bfe24e","trace_id":"872b746a-3e40-49a0-b858-c4265fb9ebf1"}
[2026-05-27 12:57:19] local.INFO: [EmailSchedule] FINISHED batch create {"host":"docker_lamp_1"} {"correlation_id":"1743fc5e-a278-45c5-989d-06afb0bfe24e","trace_id":"872b746a-3e40-49a0-b858-c4265fb9ebf1"}
[2026-05-27 12:57:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:batch:create","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"1743fc5e-a278-45c5-989d-06afb0bfe24e","trace_id":"872b746a-3e40-49a0-b858-c4265fb9ebf1"}
[2026-05-27 12:57:21] local.INFO: [Jiminny\Jobs\Mailbox\CreateBatches] processed 1 inboxes and created 0 batches {"userId":null,"batchSize":30,"maxBatches":1000} {"correlation_id":"3f9ffe79-f6b7-483d-b4e2-253da2eb120e","trace_id":"872b746a-3e40-49a0-b858-c4265fb9ebf1"}
[2026-05-27 12:57:24] 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":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
[2026-05-27 12:57:24] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
[2026-05-27 12:57:25] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
[2026-05-27 12:57:25] 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":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
[2026-05-27 12:58:05] 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":"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b","trace_id":"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743"}
[2026-05-27 12:58:05] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b","trace_id":"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743"}
[2026-05-27 12:58:05] 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":"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b","trace_id":"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743"}
[2026-05-27 12:58:07] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"90309e61-fa7c-4b5f-9c02-ce974df47319","trace_id":"26186ec1-112c-4099-a7ca-00105f5a4e64"}
[2026-05-27 12:58:07] 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":"90309e61-fa7c-4b5f-9c02-ce974df47319","trace_id":"26186ec1-112c-4099-a7ca-00105f5a4e64"}
[2026-05-27 12:58:08] local.NOTICE: Monitoring start {"correlation_id":"39cf2e27-b157-4709-b9e2-635c7ddbbd98","trace_id":"c4377397-a3de-4f5d-a07f-e92c1b0ecb7d"}
[2026-05-27 12:58:08] local.NOTICE: Monitoring end {"correlation_id":"39cf2e27-b157-4709-b9e2-635c7ddbbd98","trace_id":"c4377397-a3de-4f5d-a07f-e92c1b0ecb7d"}
[2026-05-27 12:58:11] 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":"50cef6f9-cbe0-4cbe-b1e9-68bc9ed550eb","trace_id":"c3442d24-9553-4395-a3b8-98ce1846f584"}
[2026-05-27 12:58:11] 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":"50cef6f9-cbe0-4cbe-b1e9-68bc9ed550eb","trace_id":"c3442d24-9553-4395-a3b8-98ce1846f584"}
[2026-05-27 12:58:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"f6c5ca29-2b41-4030-a550-1335fe2c4a11","trace_id":"582ee47c-7c2a-41dc-b454-b90e9f41af1a"}
[2026-05-27 12:58:13] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"f6c5ca29-2b41-4030-a550-1335fe2c4a11","trace_id":"582ee47c-7c2a-41dc-b454-b90e9f41af1a"}
[2026-05-27 12:58:13] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"f6c5ca29-2b41-4030-a550-1335fe2c4a11","trace_id":"582ee47c-7c2a-41dc-b454-b90e9f41af1a"}
[2026-05-27 12:58:13] 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":"f6c5ca29-2b41-4030-a550-1335fe2c4a11","trace_id":"582ee47c-7c2a-41dc-b454-b90e9f41af1a"}
[2026-05-27 12:58:14] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"abd5beea-c617-4fef-b30f-87b538e08cfa","trace_id":"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905"}
[2026-05-27 12:58:14] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:56:00, 2026-05-27 12:58:00] {"correlation_id":"abd5beea-c617-4fef-b30f-87b538e08cfa","trace_id":"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905"}
[2026-05-27 12:58:14] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:56:00, 2026-05-27 12:58:00] {"correlation_id":"abd5beea-c617-4fef-b30f-87b538e08cfa","trace_id":"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905"}
[2026-05-27 12:58:14] 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":"abd5beea-c617-4fef-b30f-87b538e08cfa","trace_id":"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905"}
[2026-05-27 12:58:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"calendar:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:retry-failed","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"da72e8da-2f56-4b20-bfa7-d5396d6dc5d1","trace_id":"2328e196-0c82-430d-9d17-7b14a2a06770"}
[2026-05-27 12:58:18] local.NOTICE: Calendar sync start {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:batch:retry-failed","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"da72e8da-2f56-4b20-bfa7-d5396d6dc5d1","trace_id":"2328e196-0c82-430d-9d17-7b14a2a06770"}
[2026-05-27 12:58:18] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1393,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:18] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1393,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1393,"provider":"google","refreshToken":"5aa7e2d96b53201cd16fca5d2e4ef3ad03320971fc064781d18aee3ae7b99fbf","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1393,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Account has been deleted"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1393,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1387,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1387,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1387,"provider":"google","refreshToken":"8157ac6de94842937194009e9c50e459253600f799dacf6a40755ffdbeb5bba6","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1387,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Account has been deleted"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1387,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1348,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1348,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1348,"provider":"google","refreshToken":"9e7d13d3032d0cb1b79d8e95aef01383e8e91eb52ff8ee960c8a0b6b95cd8c73","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1348,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Bad Request"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1348,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1361,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1361,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1361,"provider":"google","refreshToken":"6c843da199c2b9907445329304fcc4ec5057a4ee748d8299641764395c08e1fd","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1361,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Account has been deleted"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1361,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1310,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1310,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1310,"provider":"google","refreshToken":"e34818922c2830a660813a63f6169a4a9a992ae2cccd7dc8dd7796cfdb470ef1","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1310,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Bad Request"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1310,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1333,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1333,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1333,"provider":"google","refreshToken":"6c902986546d8e8da1dc539b046cdc1d458f519acc972e5b5f1d6a1a295165e0","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1333,"provider":"google","responseBody":{"error":"unauthorized_client","error_description":"Unauthorized"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1333,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1368,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1368,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1368,"provider":"google","refreshToken":"d2f128898ff8543bd16b69cfae37896ab85119b0f5ed2b431d739593bb600333","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1368,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Bad Request"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1368,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1365,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1365,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1365,"provider":"google","refreshToken":"7676e4a9afcd082b413248ab5ec6e487021fec6a9bdf315860a59cefad9caad8","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1365,"provider":"google","responseBody":{"error":"unauthorized_client","error_description":"Unauthorized"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1365,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1364,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1364,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1364,"provider":"google","refreshToken":"dd5882ebce76e645292ce33ae74238abbb77c0a4ecc6a2bfe723cad82e72ba8e","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1364,"provider":"google","responseBody":{"error":"unauthorized_client","error_description":"Unauthorized"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1364,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1370,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1370,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1370,"provider":"office","refreshToken":"b7ee8035306d0043cea6e00e7c4fe14f745e44074a1194db62a31cdf8b70af3e","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1370,"provider":"office","responseBody":"{\"error\":\"invalid_client\",\"error_description\":\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: e8f91f16-b4dc-40e7-985c-d4a3ac516000 Correlation ID: 0a8d37fe-9d50-4b4c-a1ae-77e0327f15fc Timestamp: 2026-05-27 12:58:21Z\",\"error_codes\":[7000215],\"timestamp\":\"2026-05-27 12:58:21Z\",\"trace_id\":\"e8f91f16-b4dc-40e7-985c-d4a3ac516000\",\"correlation_id\":\"0a8d37fe-9d50-4b4c-a1ae-77e0327f15fc\",\"error_uri\":\"https://login.microsoftonline.com/error?code=7000215\"}"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1370,"provider":"office","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1202,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1202,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1202,"provider":"office","refreshToken":"b458799ccc29b21a6e2eb5260fdb63e49ccba21bf942a3973fb63799bd7f0afe","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1202,"provider":"office","responseBody":"{\"error\":\"invalid_client\",\"error_description\":\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: 3e9be67a-9c49-4a40-ad26-4af7bae65000 Correlation ID: 7fb2008c-07fe-4705-a6ff-47e70f64b2bb Timestamp: 2026-05-27 12:58:22Z\",\"error_codes\":[7000215],\"timestamp\":\"2026-05-27 12:58:22Z\",\"trace_id\":\"3e9be67a-9c49-4a40-ad26-4af7bae65000\",\"correlation_id\":\"7fb2008c-07fe-4705-a6ff-47e70f64b2bb\",\"error_uri\":\"https://login.microsoftonline.com/error?code=7000215\"}"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1202,"provider":"office","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1502,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1502,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: Calendar sync job dispatched {"calendar_id":501} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1300,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1300,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1300,"provider":"google","refreshToken":"4b811db0725fd9602a95943519a7da935e2a5065da7d9ebfcb170752e3e1ddb8","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1300,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Account has been deleted"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1300,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1409,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1409,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1409,"provider":"google","refreshToken":"e2a3f2d06894894eed1ee87d9db1ace77d4d42ee6e1288a8940ad2c10333b0c4","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1409,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Bad Request"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1409,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1352,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1352,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1352,"provider":"google","refreshToken":"dd4b16b00fdc1216da6b717c02338c073636e29162826b2de6db3f064fc029eb","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1352,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Bad Request"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1352,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1296,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1296,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1296,"provider":"office","refreshToken":"011ae723c9d800c674e0b4be76f49fc046dac7d501b66c59ef0d9549cfa56ae5","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1296,"provider":"office","responseBody":"{\"error\":\"invalid_client\",\"error_description\":\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: cbdfd43b-d682-4397-8a35-69af29c81300 Correlation ID: 723e1e62-f593-454d-b972-845d22c92271 Timestamp: 2026-05-27 12:58:23Z\",\"error_codes\":[7000215],\"timestamp\":\"2026-05-27 12:58:23Z\",\"trace_id\":\"cbdfd43b-d682-4397-8a35-69af29c81300\",\"correlation_id\":\"723e1e62-f593-454d-b972-845d22c92271\",\"error_uri\":\"https://login.microsoftonline.com/error?code=7000215\"}"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1296,"provider":"office","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":391,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":391,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":391,"provider":"office","refreshToken":"00045eebae0f39b34887c6d53f92ae78064f7145e1f4b67754aebd03cfb2d881","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":391,"provider":"office","responseBody":"{\"error\":\"invalid_client\",\"error_description\":\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: f559a2e0-ad9d-4163-bd2c-dcf7c33f1200 Correlation ID: 44266705-aff3-4701-aa5e-a88b7867593a Timestamp: 2026-05-27 12:58:23Z\",\"error_codes\":[7000215],\"timestamp\":\"2026-05-27 12:58:23Z\",\"trace_id\":\"f559a2e0-ad9d-4163...
|
[{"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},{"role":"AXStaticText","text":"10","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 12:57:12] local.NOTICE: Monitoring start {\"correlation_id\":\"01b85e02-17ff-44f0-8d6e-484a810b1085\",\"trace_id\":\"b4d68a19-67e7-41f9-a2f3-ca98f84e617b\"}\n[2026-05-27 12:57:12] local.NOTICE: Monitoring end {\"correlation_id\":\"01b85e02-17ff-44f0-8d6e-484a810b1085\",\"trace_id\":\"b4d68a19-67e7-41f9-a2f3-ca98f84e617b\"}\n[2026-05-27 12:57: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\":\"829043ea-15e1-40fe-a7ce-27fc8440984e\",\"trace_id\":\"bd9f99cc-933a-453b-b9ff-869d04e9b49d\"}\n[2026-05-27 12:57: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\":\"829043ea-15e1-40fe-a7ce-27fc8440984e\",\"trace_id\":\"bd9f99cc-933a-453b-b9ff-869d04e9b49d\"}\n[2026-05-27 12:57: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\":\"eb849911-4661-4753-b85d-3bd9dd3b0b9d\",\"trace_id\":\"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a\"}\n[2026-05-27 12:57:15] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"eb849911-4661-4753-b85d-3bd9dd3b0b9d\",\"trace_id\":\"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a\"}\n[2026-05-27 12:57:15] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"eb849911-4661-4753-b85d-3bd9dd3b0b9d\",\"trace_id\":\"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a\"}\n[2026-05-27 12:57: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\":\"eb849911-4661-4753-b85d-3bd9dd3b0b9d\",\"trace_id\":\"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a\"}\n[2026-05-27 12:57:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:create\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"1743fc5e-a278-45c5-989d-06afb0bfe24e\",\"trace_id\":\"872b746a-3e40-49a0-b858-c4265fb9ebf1\"}\n[2026-05-27 12:57:19] local.INFO: [EmailSchedule] STARTING batch create {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1743fc5e-a278-45c5-989d-06afb0bfe24e\",\"trace_id\":\"872b746a-3e40-49a0-b858-c4265fb9ebf1\"}\n[2026-05-27 12:57:19] local.INFO: [EmailSchedule] FINISHED batch create {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1743fc5e-a278-45c5-989d-06afb0bfe24e\",\"trace_id\":\"872b746a-3e40-49a0-b858-c4265fb9ebf1\"}\n[2026-05-27 12:57:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:create\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"1743fc5e-a278-45c5-989d-06afb0bfe24e\",\"trace_id\":\"872b746a-3e40-49a0-b858-c4265fb9ebf1\"}\n[2026-05-27 12:57:21] local.INFO: [Jiminny\\Jobs\\Mailbox\\CreateBatches] processed 1 inboxes and created 0 batches {\"userId\":null,\"batchSize\":30,\"maxBatches\":1000} {\"correlation_id\":\"3f9ffe79-f6b7-483d-b4e2-253da2eb120e\",\"trace_id\":\"872b746a-3e40-49a0-b858-c4265fb9ebf1\"}\n[2026-05-27 12:57:24] 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\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}\n[2026-05-27 12:57:24] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}\n[2026-05-27 12:57:25] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}\n[2026-05-27 12:57:25] 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\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}\n[2026-05-27 12:58:05] 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\":\"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b\",\"trace_id\":\"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743\"}\n[2026-05-27 12:58:05] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b\",\"trace_id\":\"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743\"}\n[2026-05-27 12:58:05] 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\":\"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b\",\"trace_id\":\"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743\"}\n[2026-05-27 12:58:07] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"90309e61-fa7c-4b5f-9c02-ce974df47319\",\"trace_id\":\"26186ec1-112c-4099-a7ca-00105f5a4e64\"}\n[2026-05-27 12:58:07] 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\":\"90309e61-fa7c-4b5f-9c02-ce974df47319\",\"trace_id\":\"26186ec1-112c-4099-a7ca-00105f5a4e64\"}\n[2026-05-27 12:58:08] local.NOTICE: Monitoring start {\"correlation_id\":\"39cf2e27-b157-4709-b9e2-635c7ddbbd98\",\"trace_id\":\"c4377397-a3de-4f5d-a07f-e92c1b0ecb7d\"}\n[2026-05-27 12:58:08] local.NOTICE: Monitoring end {\"correlation_id\":\"39cf2e27-b157-4709-b9e2-635c7ddbbd98\",\"trace_id\":\"c4377397-a3de-4f5d-a07f-e92c1b0ecb7d\"}\n[2026-05-27 12:58:11] 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\":\"50cef6f9-cbe0-4cbe-b1e9-68bc9ed550eb\",\"trace_id\":\"c3442d24-9553-4395-a3b8-98ce1846f584\"}\n[2026-05-27 12:58:11] 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\":\"50cef6f9-cbe0-4cbe-b1e9-68bc9ed550eb\",\"trace_id\":\"c3442d24-9553-4395-a3b8-98ce1846f584\"}\n[2026-05-27 12:58:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"f6c5ca29-2b41-4030-a550-1335fe2c4a11\",\"trace_id\":\"582ee47c-7c2a-41dc-b454-b90e9f41af1a\"}\n[2026-05-27 12:58:13] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"f6c5ca29-2b41-4030-a550-1335fe2c4a11\",\"trace_id\":\"582ee47c-7c2a-41dc-b454-b90e9f41af1a\"}\n[2026-05-27 12:58:13] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"f6c5ca29-2b41-4030-a550-1335fe2c4a11\",\"trace_id\":\"582ee47c-7c2a-41dc-b454-b90e9f41af1a\"}\n[2026-05-27 12:58:13] 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\":\"f6c5ca29-2b41-4030-a550-1335fe2c4a11\",\"trace_id\":\"582ee47c-7c2a-41dc-b454-b90e9f41af1a\"}\n[2026-05-27 12:58:14] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"abd5beea-c617-4fef-b30f-87b538e08cfa\",\"trace_id\":\"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905\"}\n[2026-05-27 12:58:14] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:56:00, 2026-05-27 12:58:00] {\"correlation_id\":\"abd5beea-c617-4fef-b30f-87b538e08cfa\",\"trace_id\":\"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905\"}\n[2026-05-27 12:58:14] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:56:00, 2026-05-27 12:58:00] {\"correlation_id\":\"abd5beea-c617-4fef-b30f-87b538e08cfa\",\"trace_id\":\"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905\"}\n[2026-05-27 12:58:14] 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\":\"abd5beea-c617-4fef-b30f-87b538e08cfa\",\"trace_id\":\"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905\"}\n[2026-05-27 12:58:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"calendar:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:retry-failed\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"da72e8da-2f56-4b20-bfa7-d5396d6dc5d1\",\"trace_id\":\"2328e196-0c82-430d-9d17-7b14a2a06770\"}\n[2026-05-27 12:58:18] local.NOTICE: Calendar sync start {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:retry-failed\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"da72e8da-2f56-4b20-bfa7-d5396d6dc5d1\",\"trace_id\":\"2328e196-0c82-430d-9d17-7b14a2a06770\"}\n[2026-05-27 12:58:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1393,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:18] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1393,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1393,\"provider\":\"google\",\"refreshToken\":\"5aa7e2d96b53201cd16fca5d2e4ef3ad03320971fc064781d18aee3ae7b99fbf\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1393,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Account has been deleted\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1393,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1387,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1387,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1387,\"provider\":\"google\",\"refreshToken\":\"8157ac6de94842937194009e9c50e459253600f799dacf6a40755ffdbeb5bba6\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1387,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Account has been deleted\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1387,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1348,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1348,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1348,\"provider\":\"google\",\"refreshToken\":\"9e7d13d3032d0cb1b79d8e95aef01383e8e91eb52ff8ee960c8a0b6b95cd8c73\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1348,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1348,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1361,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1361,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1361,\"provider\":\"google\",\"refreshToken\":\"6c843da199c2b9907445329304fcc4ec5057a4ee748d8299641764395c08e1fd\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1361,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Account has been deleted\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1361,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1310,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1310,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1310,\"provider\":\"google\",\"refreshToken\":\"e34818922c2830a660813a63f6169a4a9a992ae2cccd7dc8dd7796cfdb470ef1\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1310,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1310,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1333,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1333,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1333,\"provider\":\"google\",\"refreshToken\":\"6c902986546d8e8da1dc539b046cdc1d458f519acc972e5b5f1d6a1a295165e0\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1333,\"provider\":\"google\",\"responseBody\":{\"error\":\"unauthorized_client\",\"error_description\":\"Unauthorized\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1333,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1368,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1368,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1368,\"provider\":\"google\",\"refreshToken\":\"d2f128898ff8543bd16b69cfae37896ab85119b0f5ed2b431d739593bb600333\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1368,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1368,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1365,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1365,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1365,\"provider\":\"google\",\"refreshToken\":\"7676e4a9afcd082b413248ab5ec6e487021fec6a9bdf315860a59cefad9caad8\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1365,\"provider\":\"google\",\"responseBody\":{\"error\":\"unauthorized_client\",\"error_description\":\"Unauthorized\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1365,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1364,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1364,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1364,\"provider\":\"google\",\"refreshToken\":\"dd5882ebce76e645292ce33ae74238abbb77c0a4ecc6a2bfe723cad82e72ba8e\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1364,\"provider\":\"google\",\"responseBody\":{\"error\":\"unauthorized_client\",\"error_description\":\"Unauthorized\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1364,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1370,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1370,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1370,\"provider\":\"office\",\"refreshToken\":\"b7ee8035306d0043cea6e00e7c4fe14f745e44074a1194db62a31cdf8b70af3e\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1370,\"provider\":\"office\",\"responseBody\":\"{\\\"error\\\":\\\"invalid_client\\\",\\\"error_description\\\":\\\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: e8f91f16-b4dc-40e7-985c-d4a3ac516000 Correlation ID: 0a8d37fe-9d50-4b4c-a1ae-77e0327f15fc Timestamp: 2026-05-27 12:58:21Z\\\",\\\"error_codes\\\":[7000215],\\\"timestamp\\\":\\\"2026-05-27 12:58:21Z\\\",\\\"trace_id\\\":\\\"e8f91f16-b4dc-40e7-985c-d4a3ac516000\\\",\\\"correlation_id\\\":\\\"0a8d37fe-9d50-4b4c-a1ae-77e0327f15fc\\\",\\\"error_uri\\\":\\\"https://login.microsoftonline.com/error?code=7000215\\\"}\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1370,\"provider\":\"office\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1202,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1202,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1202,\"provider\":\"office\",\"refreshToken\":\"b458799ccc29b21a6e2eb5260fdb63e49ccba21bf942a3973fb63799bd7f0afe\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1202,\"provider\":\"office\",\"responseBody\":\"{\\\"error\\\":\\\"invalid_client\\\",\\\"error_description\\\":\\\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: 3e9be67a-9c49-4a40-ad26-4af7bae65000 Correlation ID: 7fb2008c-07fe-4705-a6ff-47e70f64b2bb Timestamp: 2026-05-27 12:58:22Z\\\",\\\"error_codes\\\":[7000215],\\\"timestamp\\\":\\\"2026-05-27 12:58:22Z\\\",\\\"trace_id\\\":\\\"3e9be67a-9c49-4a40-ad26-4af7bae65000\\\",\\\"correlation_id\\\":\\\"7fb2008c-07fe-4705-a6ff-47e70f64b2bb\\\",\\\"error_uri\\\":\\\"https://login.microsoftonline.com/error?code=7000215\\\"}\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1202,\"provider\":\"office\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: Calendar sync job dispatched {\"calendar_id\":501} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1300,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1300,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1300,\"provider\":\"google\",\"refreshToken\":\"4b811db0725fd9602a95943519a7da935e2a5065da7d9ebfcb170752e3e1ddb8\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1300,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Account has been deleted\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1300,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1409,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1409,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1409,\"provider\":\"google\",\"refreshToken\":\"e2a3f2d06894894eed1ee87d9db1ace77d4d42ee6e1288a8940ad2c10333b0c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1409,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1409,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1352,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1352,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1352,\"provider\":\"google\",\"refreshToken\":\"dd4b16b00fdc1216da6b717c02338c073636e29162826b2de6db3f064fc029eb\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1352,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1352,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1296,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1296,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1296,\"provider\":\"office\",\"refreshToken\":\"011ae723c9d800c674e0b4be76f49fc046dac7d501b66c59ef0d9549cfa56ae5\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1296,\"provider\":\"office\",\"responseBody\":\"{\\\"error\\\":\\\"invalid_client\\\",\\\"error_description\\\":\\\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: cbdfd43b-d682-4397-8a35-69af29c81300 Correlation ID: 723e1e62-f593-454d-b972-845d22c92271 Timestamp: 2026-05-27 12:58:23Z\\\",\\\"error_codes\\\":[7000215],\\\"timestamp\\\":\\\"2026-05-27 12:58:23Z\\\",\\\"trace_id\\\":\\\"cbdfd43b-d682-4397-8a35-69af29c81300\\\",\\\"correlation_id\\\":\\\"723e1e62-f593-454d-b972-845d22c92271\\\",\\\"error_uri\\\":\\\"https://login.microsoftonline.com/error?code=7000215\\\"}\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1296,\"provider\":\"office\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":391,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":391,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":391,\"provider\":\"office\",\"refreshToken\":\"00045eebae0f39b34887c6d53f92ae78064f7145e1f4b67754aebd03cfb2d881\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":391,\"provider\":\"office\",\"responseBody\":\"{\\\"error\\\":\\\"invalid_client\\\",\\\"error_description\\\":\\\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: f559a2e0-ad9d-4163-bd2c-dcf7c33f1200 Correlation ID: 44266705-aff3-4701-aa5e-a88b7867593a Timestamp: 2026-05-27 12:58:23Z\\\",\\\"error_codes\\\":[7000215],\\\"timestamp\\\":\\\"2026-05-27 12:58:23Z\\\",\\\"trace_id\\\":\\\"f559a2e0-ad9d-4163-bd2c-dcf7c33f1200\\\",\\\"correlation_id\\\":\\\"44266705-aff3-4701-aa5e-a88b7867593a\\\",\\\"error_uri\\\":\\\"https://login.microsoftonline.com/error?code=7000215\\\"}\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":391,\"provider\":\"office\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1271,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1271,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1271,\"provider\":\"office\",\"refreshToken\":\"118cde2c06993147b07ccaec4cbcd5026a819dea6c71081166a492933e392afb\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1271,\"provider\":\"office\",\"responseBody\":\"{\\\"error\\\":\\\"invalid_client\\\",\\\"error_description\\\":\\\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: cac48412-52b9-4708-8b76-a4c08ddd6e00 Correlation ID: 7e027d28-b370-4b8a-b6ae-996d6de1eb35 Timestamp: 2026-05-27 12:58:24Z\\\",\\\"error_codes\\\":[7000215],\\\"timestamp\\\":\\\"2026-05-27 12:58:24Z\\\",\\\"trace_id\\\":\\\"cac48412-52b9-4708-8b76-a4c08ddd6e00\\\",\\\"correlation_id\\\":\\\"7e027d28-b370-4b8a-b6ae-996d6de1eb35\\\",\\\"error_uri\\\":\\\"https://login.microsoftonline.com/error?code=7000215\\\"}\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1271,\"provider\":\"office\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1351,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1351,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1351,\"provider\":\"google\",\"refreshToken\":\"4271d15b9e60a606439caddc68337f783e472c85b03dacff14d1b6dfded9051c\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [Calendar] Processing sync {\"calendarId\":\"a33076c1-8d97-431a-99f0-85c9524e118b\",\"from\":null,\"to\":null,\"delta\":\"CIiFh8TP44kDEIiFh8TP44kDGAUgkZvkzgIokZvkzgI=\",\"last_sync\":\"2024-12-09 07:12:53\",\"dateMode\":\"daily\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"integration-app\",\"crm_owner\":1695,\"team_id\":3143} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1351,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1351,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1366,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1366,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1366,\"provider\":\"google\",\"refreshToken\":\"ae21385059b2eebfd43f68aecd56eccd702a1aabb6598f1f7ab594ed8af491b4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1366,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1366,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: Calendar sync job dispatched {\"calendar_id\":378} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1421,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1421,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: Calendar sync job dispatched {\"calendar_id\":504} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.NOTICE: Calendar sync end {\"retrieved_calendars\":31,\"processed_calendars\":3} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"calendar:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [Google Calendar] Failed to watch channel for calendar {\"calendarId\":\"a33076c1-8d97-431a-99f0-85c9524e118b\",\"code\":400,\"reason\":\"{\n \\\"error\\\": {\n \\\"errors\\\": [\n {\n \\\"domain\\\": \\\"global\\\",\n \\\"reason\\\": \\\"push.webhookUrlNotHttps\\\",\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n ],\n \\\"code\\\": 400,\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n}\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.WARNING: [Calendar] Sync failed {\"calendarId\":\"a33076c1-8d97-431a-99f0-85c9524e118b\",\"code\":400,\"reason\":\"{\n \\\"error\\\": {\n \\\"errors\\\": [\n {\n \\\"domain\\\": \\\"global\\\",\n \\\"reason\\\": \\\"push.webhookUrlNotHttps\\\",\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n ],\n \\\"code\\\": 400,\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n}\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [Calendar] Processing sync {\"calendarId\":\"2676cb6d-f86c-427e-bf78-591e388e3c1e\",\"from\":null,\"to\":null,\"delta\":\"CJ_x49O3jpIDEJ_x49O3jpIDGAUgw67KlwMow67KlwM=\",\"last_sync\":\"2026-01-19 07:48:40\",\"dateMode\":\"daily\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.WARNING: [Pipedrive] Account not connected for user {\"userId\":\"e6538737-e7b4-455f-a37a-3e79b665a220\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":1116,\"sociable_id\":241,\"provider_user_id\":\"19555731\",\"expires\":1779091997,\"refresh_token_expires\":null,\"provider\":\"pipedrive\",\"state\":\"full-refresh\",\"auth_scope\":\"base,deals:full,activities:full,contacts:full,search:read\",\"retry_after\":null,\"created_at\":\"2023-09-08 09:44:29\",\"updated_at\":\"2026-05-18 08:13:32\"}}} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"pipedrive\",\"crm_owner\":241,\"team_id\":19} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"pipedrive\",\"team_id\":19} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"pipedrive\",\"team_id\":19} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.WARNING: [Calendar] CRM disconnected for user so events will not be matched {\"provider\":\"pipedrive\",\"user_id\":241,\"message\":\"Your Pipedrive account has become disconnected. Please login to Jiminny to reconnect.\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [Google Calendar] Failed to watch channel for calendar {\"calendarId\":\"2676cb6d-f86c-427e-bf78-591e388e3c1e\",\"code\":400,\"reason\":\"{\n \\\"error\\\": {\n \\\"errors\\\": [\n {\n \\\"domain\\\": \\\"global\\\",\n \\\"reason\\\": \\\"push.webhookUrlNotHttps\\\",\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n ],\n \\\"code\\\": 400,\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n}\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.WARNING: [Calendar] Sync failed {\"calendarId\":\"2676cb6d-f86c-427e-bf78-591e388e3c1e\",\"code\":400,\"reason\":\"{\n \\\"error\\\": {\n \\\"errors\\\": [\n {\n \\\"domain\\\": \\\"global\\\",\n \\\"reason\\\": \\\"push.webhookUrlNotHttps\\\",\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n ],\n \\\"code\\\": 400,\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n}\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1421,\"provider\":\"office\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1421,\"provider\":\"office\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [Calendar] Processing sync {\"calendarId\":\"9e8b1a2c-1a8f-42bd-b161-810fc0baf540\",\"from\":null,\"to\":null,\"delta\":\"R0usmcdvmMuZCBYV0hguCLbinXNUdndbb-ECUsN4LJrJG3mny6y_meJS8MAAHR5ZMrcUO3h48ocZNNnE7oP5gw1vlpVCP8G7vwKiJvMr__pTveeFSIgIX26vx0F0cDUmNI0_JTiUjIa8SS87NYFu0VC-K9UUoS_3YAmb3QbL1FzAfSGSNtasPG9UoNDP2sTOfaa0y-bXW7hGxDeemyMi5Q.mq5epx6DKMR2Kr4O2JnKftifGVfsgrCAgaghMEclE4U\",\"last_sync\":\"2026-05-27 06:58:28\",\"dateMode\":\"daily\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":89,\"team_id\":2} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [MS Office Calendar] Skipping delta sync for daily mode {\"calendarId\":\"9e8b1a2c-1a8f-42bd-b161-810fc0baf540\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}","depth":4,"on_screen":true,"value":"[2026-05-27 12:57:12] local.NOTICE: Monitoring start {\"correlation_id\":\"01b85e02-17ff-44f0-8d6e-484a810b1085\",\"trace_id\":\"b4d68a19-67e7-41f9-a2f3-ca98f84e617b\"}\n[2026-05-27 12:57:12] local.NOTICE: Monitoring end {\"correlation_id\":\"01b85e02-17ff-44f0-8d6e-484a810b1085\",\"trace_id\":\"b4d68a19-67e7-41f9-a2f3-ca98f84e617b\"}\n[2026-05-27 12:57: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\":\"829043ea-15e1-40fe-a7ce-27fc8440984e\",\"trace_id\":\"bd9f99cc-933a-453b-b9ff-869d04e9b49d\"}\n[2026-05-27 12:57: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\":\"829043ea-15e1-40fe-a7ce-27fc8440984e\",\"trace_id\":\"bd9f99cc-933a-453b-b9ff-869d04e9b49d\"}\n[2026-05-27 12:57: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\":\"eb849911-4661-4753-b85d-3bd9dd3b0b9d\",\"trace_id\":\"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a\"}\n[2026-05-27 12:57:15] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"eb849911-4661-4753-b85d-3bd9dd3b0b9d\",\"trace_id\":\"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a\"}\n[2026-05-27 12:57:15] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"eb849911-4661-4753-b85d-3bd9dd3b0b9d\",\"trace_id\":\"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a\"}\n[2026-05-27 12:57: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\":\"eb849911-4661-4753-b85d-3bd9dd3b0b9d\",\"trace_id\":\"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a\"}\n[2026-05-27 12:57:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:create\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"1743fc5e-a278-45c5-989d-06afb0bfe24e\",\"trace_id\":\"872b746a-3e40-49a0-b858-c4265fb9ebf1\"}\n[2026-05-27 12:57:19] local.INFO: [EmailSchedule] STARTING batch create {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1743fc5e-a278-45c5-989d-06afb0bfe24e\",\"trace_id\":\"872b746a-3e40-49a0-b858-c4265fb9ebf1\"}\n[2026-05-27 12:57:19] local.INFO: [EmailSchedule] FINISHED batch create {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1743fc5e-a278-45c5-989d-06afb0bfe24e\",\"trace_id\":\"872b746a-3e40-49a0-b858-c4265fb9ebf1\"}\n[2026-05-27 12:57:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:create\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"1743fc5e-a278-45c5-989d-06afb0bfe24e\",\"trace_id\":\"872b746a-3e40-49a0-b858-c4265fb9ebf1\"}\n[2026-05-27 12:57:21] local.INFO: [Jiminny\\Jobs\\Mailbox\\CreateBatches] processed 1 inboxes and created 0 batches {\"userId\":null,\"batchSize\":30,\"maxBatches\":1000} {\"correlation_id\":\"3f9ffe79-f6b7-483d-b4e2-253da2eb120e\",\"trace_id\":\"872b746a-3e40-49a0-b858-c4265fb9ebf1\"}\n[2026-05-27 12:57:24] 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\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}\n[2026-05-27 12:57:24] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}\n[2026-05-27 12:57:25] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}\n[2026-05-27 12:57:25] 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\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}\n[2026-05-27 12:58:05] 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\":\"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b\",\"trace_id\":\"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743\"}\n[2026-05-27 12:58:05] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b\",\"trace_id\":\"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743\"}\n[2026-05-27 12:58:05] 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\":\"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b\",\"trace_id\":\"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743\"}\n[2026-05-27 12:58:07] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"90309e61-fa7c-4b5f-9c02-ce974df47319\",\"trace_id\":\"26186ec1-112c-4099-a7ca-00105f5a4e64\"}\n[2026-05-27 12:58:07] 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\":\"90309e61-fa7c-4b5f-9c02-ce974df47319\",\"trace_id\":\"26186ec1-112c-4099-a7ca-00105f5a4e64\"}\n[2026-05-27 12:58:08] local.NOTICE: Monitoring start {\"correlation_id\":\"39cf2e27-b157-4709-b9e2-635c7ddbbd98\",\"trace_id\":\"c4377397-a3de-4f5d-a07f-e92c1b0ecb7d\"}\n[2026-05-27 12:58:08] local.NOTICE: Monitoring end {\"correlation_id\":\"39cf2e27-b157-4709-b9e2-635c7ddbbd98\",\"trace_id\":\"c4377397-a3de-4f5d-a07f-e92c1b0ecb7d\"}\n[2026-05-27 12:58:11] 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\":\"50cef6f9-cbe0-4cbe-b1e9-68bc9ed550eb\",\"trace_id\":\"c3442d24-9553-4395-a3b8-98ce1846f584\"}\n[2026-05-27 12:58:11] 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\":\"50cef6f9-cbe0-4cbe-b1e9-68bc9ed550eb\",\"trace_id\":\"c3442d24-9553-4395-a3b8-98ce1846f584\"}\n[2026-05-27 12:58:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"f6c5ca29-2b41-4030-a550-1335fe2c4a11\",\"trace_id\":\"582ee47c-7c2a-41dc-b454-b90e9f41af1a\"}\n[2026-05-27 12:58:13] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"f6c5ca29-2b41-4030-a550-1335fe2c4a11\",\"trace_id\":\"582ee47c-7c2a-41dc-b454-b90e9f41af1a\"}\n[2026-05-27 12:58:13] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"f6c5ca29-2b41-4030-a550-1335fe2c4a11\",\"trace_id\":\"582ee47c-7c2a-41dc-b454-b90e9f41af1a\"}\n[2026-05-27 12:58:13] 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\":\"f6c5ca29-2b41-4030-a550-1335fe2c4a11\",\"trace_id\":\"582ee47c-7c2a-41dc-b454-b90e9f41af1a\"}\n[2026-05-27 12:58:14] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"abd5beea-c617-4fef-b30f-87b538e08cfa\",\"trace_id\":\"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905\"}\n[2026-05-27 12:58:14] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:56:00, 2026-05-27 12:58:00] {\"correlation_id\":\"abd5beea-c617-4fef-b30f-87b538e08cfa\",\"trace_id\":\"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905\"}\n[2026-05-27 12:58:14] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:56:00, 2026-05-27 12:58:00] {\"correlation_id\":\"abd5beea-c617-4fef-b30f-87b538e08cfa\",\"trace_id\":\"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905\"}\n[2026-05-27 12:58:14] 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\":\"abd5beea-c617-4fef-b30f-87b538e08cfa\",\"trace_id\":\"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905\"}\n[2026-05-27 12:58:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"calendar:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:retry-failed\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"da72e8da-2f56-4b20-bfa7-d5396d6dc5d1\",\"trace_id\":\"2328e196-0c82-430d-9d17-7b14a2a06770\"}\n[2026-05-27 12:58:18] local.NOTICE: Calendar sync start {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:retry-failed\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"da72e8da-2f56-4b20-bfa7-d5396d6dc5d1\",\"trace_id\":\"2328e196-0c82-430d-9d17-7b14a2a06770\"}\n[2026-05-27 12:58:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1393,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:18] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1393,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1393,\"provider\":\"google\",\"refreshToken\":\"5aa7e2d96b53201cd16fca5d2e4ef3ad03320971fc064781d18aee3ae7b99fbf\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1393,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Account has been deleted\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1393,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1387,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1387,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1387,\"provider\":\"google\",\"refreshToken\":\"8157ac6de94842937194009e9c50e459253600f799dacf6a40755ffdbeb5bba6\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1387,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Account has been deleted\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1387,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1348,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1348,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1348,\"provider\":\"google\",\"refreshToken\":\"9e7d13d3032d0cb1b79d8e95aef01383e8e91eb52ff8ee960c8a0b6b95cd8c73\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1348,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1348,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1361,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1361,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1361,\"provider\":\"google\",\"refreshToken\":\"6c843da199c2b9907445329304fcc4ec5057a4ee748d8299641764395c08e1fd\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1361,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Account has been deleted\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1361,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1310,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1310,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1310,\"provider\":\"google\",\"refreshToken\":\"e34818922c2830a660813a63f6169a4a9a992ae2cccd7dc8dd7796cfdb470ef1\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1310,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1310,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1333,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1333,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1333,\"provider\":\"google\",\"refreshToken\":\"6c902986546d8e8da1dc539b046cdc1d458f519acc972e5b5f1d6a1a295165e0\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1333,\"provider\":\"google\",\"responseBody\":{\"error\":\"unauthorized_client\",\"error_description\":\"Unauthorized\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1333,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1368,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1368,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1368,\"provider\":\"google\",\"refreshToken\":\"d2f128898ff8543bd16b69cfae37896ab85119b0f5ed2b431d739593bb600333\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1368,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1368,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1365,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1365,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1365,\"provider\":\"google\",\"refreshToken\":\"7676e4a9afcd082b413248ab5ec6e487021fec6a9bdf315860a59cefad9caad8\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1365,\"provider\":\"google\",\"responseBody\":{\"error\":\"unauthorized_client\",\"error_description\":\"Unauthorized\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1365,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1364,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1364,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1364,\"provider\":\"google\",\"refreshToken\":\"dd5882ebce76e645292ce33ae74238abbb77c0a4ecc6a2bfe723cad82e72ba8e\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1364,\"provider\":\"google\",\"responseBody\":{\"error\":\"unauthorized_client\",\"error_description\":\"Unauthorized\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1364,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1370,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1370,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1370,\"provider\":\"office\",\"refreshToken\":\"b7ee8035306d0043cea6e00e7c4fe14f745e44074a1194db62a31cdf8b70af3e\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1370,\"provider\":\"office\",\"responseBody\":\"{\\\"error\\\":\\\"invalid_client\\\",\\\"error_description\\\":\\\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: e8f91f16-b4dc-40e7-985c-d4a3ac516000 Correlation ID: 0a8d37fe-9d50-4b4c-a1ae-77e0327f15fc Timestamp: 2026-05-27 12:58:21Z\\\",\\\"error_codes\\\":[7000215],\\\"timestamp\\\":\\\"2026-05-27 12:58:21Z\\\",\\\"trace_id\\\":\\\"e8f91f16-b4dc-40e7-985c-d4a3ac516000\\\",\\\"correlation_id\\\":\\\"0a8d37fe-9d50-4b4c-a1ae-77e0327f15fc\\\",\\\"error_uri\\\":\\\"https://login.microsoftonline.com/error?code=7000215\\\"}\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1370,\"provider\":\"office\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1202,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1202,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1202,\"provider\":\"office\",\"refreshToken\":\"b458799ccc29b21a6e2eb5260fdb63e49ccba21bf942a3973fb63799bd7f0afe\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1202,\"provider\":\"office\",\"responseBody\":\"{\\\"error\\\":\\\"invalid_client\\\",\\\"error_description\\\":\\\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: 3e9be67a-9c49-4a40-ad26-4af7bae65000 Correlation ID: 7fb2008c-07fe-4705-a6ff-47e70f64b2bb Timestamp: 2026-05-27 12:58:22Z\\\",\\\"error_codes\\\":[7000215],\\\"timestamp\\\":\\\"2026-05-27 12:58:22Z\\\",\\\"trace_id\\\":\\\"3e9be67a-9c49-4a40-ad26-4af7bae65000\\\",\\\"correlation_id\\\":\\\"7fb2008c-07fe-4705-a6ff-47e70f64b2bb\\\",\\\"error_uri\\\":\\\"https://login.microsoftonline.com/error?code=7000215\\\"}\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1202,\"provider\":\"office\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: Calendar sync job dispatched {\"calendar_id\":501} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1300,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1300,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1300,\"provider\":\"google\",\"refreshToken\":\"4b811db0725fd9602a95943519a7da935e2a5065da7d9ebfcb170752e3e1ddb8\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1300,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Account has been deleted\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1300,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1409,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1409,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1409,\"provider\":\"google\",\"refreshToken\":\"e2a3f2d06894894eed1ee87d9db1ace77d4d42ee6e1288a8940ad2c10333b0c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1409,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1409,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1352,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1352,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1352,\"provider\":\"google\",\"refreshToken\":\"dd4b16b00fdc1216da6b717c02338c073636e29162826b2de6db3f064fc029eb\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1352,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1352,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1296,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1296,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1296,\"provider\":\"office\",\"refreshToken\":\"011ae723c9d800c674e0b4be76f49fc046dac7d501b66c59ef0d9549cfa56ae5\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1296,\"provider\":\"office\",\"responseBody\":\"{\\\"error\\\":\\\"invalid_client\\\",\\\"error_description\\\":\\\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: cbdfd43b-d682-4397-8a35-69af29c81300 Correlation ID: 723e1e62-f593-454d-b972-845d22c92271 Timestamp: 2026-05-27 12:58:23Z\\\",\\\"error_codes\\\":[7000215],\\\"timestamp\\\":\\\"2026-05-27 12:58:23Z\\\",\\\"trace_id\\\":\\\"cbdfd43b-d682-4397-8a35-69af29c81300\\\",\\\"correlation_id\\\":\\\"723e1e62-f593-454d-b972-845d22c92271\\\",\\\"error_uri\\\":\\\"https://login.microsoftonline.com/error?code=7000215\\\"}\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1296,\"provider\":\"office\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":391,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":391,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":391,\"provider\":\"office\",\"refreshToken\":\"00045eebae0f39b34887c6d53f92ae78064f7145e1f4b67754aebd03cfb2d881\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":391,\"provider\":\"office\",\"responseBody\":\"{\\\"error\\\":\\\"invalid_client\\\",\\\"error_description\\\":\\\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: f559a2e0-ad9d-4163-bd2c-dcf7c33f1200 Correlation ID: 44266705-aff3-4701-aa5e-a88b7867593a Timestamp: 2026-05-27 12:58:23Z\\\",\\\"error_codes\\\":[7000215],\\\"timestamp\\\":\\\"2026-05-27 12:58:23Z\\\",\\\"trace_id\\\":\\\"f559a2e0-ad9d-4163-bd2c-dcf7c33f1200\\\",\\\"correlation_id\\\":\\\"44266705-aff3-4701-aa5e-a88b7867593a\\\",\\\"error_uri\\\":\\\"https://login.microsoftonline.com/error?code=7000215\\\"}\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":391,\"provider\":\"office\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1271,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1271,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1271,\"provider\":\"office\",\"refreshToken\":\"118cde2c06993147b07ccaec4cbcd5026a819dea6c71081166a492933e392afb\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1271,\"provider\":\"office\",\"responseBody\":\"{\\\"error\\\":\\\"invalid_client\\\",\\\"error_description\\\":\\\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: cac48412-52b9-4708-8b76-a4c08ddd6e00 Correlation ID: 7e027d28-b370-4b8a-b6ae-996d6de1eb35 Timestamp: 2026-05-27 12:58:24Z\\\",\\\"error_codes\\\":[7000215],\\\"timestamp\\\":\\\"2026-05-27 12:58:24Z\\\",\\\"trace_id\\\":\\\"cac48412-52b9-4708-8b76-a4c08ddd6e00\\\",\\\"correlation_id\\\":\\\"7e027d28-b370-4b8a-b6ae-996d6de1eb35\\\",\\\"error_uri\\\":\\\"https://login.microsoftonline.com/error?code=7000215\\\"}\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1271,\"provider\":\"office\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1351,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1351,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1351,\"provider\":\"google\",\"refreshToken\":\"4271d15b9e60a606439caddc68337f783e472c85b03dacff14d1b6dfded9051c\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [Calendar] Processing sync {\"calendarId\":\"a33076c1-8d97-431a-99f0-85c9524e118b\",\"from\":null,\"to\":null,\"delta\":\"CIiFh8TP44kDEIiFh8TP44kDGAUgkZvkzgIokZvkzgI=\",\"last_sync\":\"2024-12-09 07:12:53\",\"dateMode\":\"daily\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"integration-app\",\"crm_owner\":1695,\"team_id\":3143} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1502,\"provider\":\"google\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1351,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1351,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1366,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1366,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1366,\"provider\":\"google\",\"refreshToken\":\"ae21385059b2eebfd43f68aecd56eccd702a1aabb6598f1f7ab594ed8af491b4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1366,\"provider\":\"google\",\"responseBody\":{\"error\":\"invalid_grant\",\"error_description\":\"Bad Request\"}} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.ERROR: [SocialAccountService] Failed to refresh token {\"socialAccountId\":1366,\"provider\":\"google\",\"reason\":\"Flow refresh required.\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: Calendar sync job dispatched {\"calendar_id\":378} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1421,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:24] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1421,\"provider\":\"office\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: Calendar sync job dispatched {\"calendar_id\":504} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.NOTICE: Calendar sync end {\"retrieved_calendars\":31,\"processed_calendars\":3} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"calendar:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"b939891c-4c61-4fc3-b7d5-4bcf72084fce\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [Google Calendar] Failed to watch channel for calendar {\"calendarId\":\"a33076c1-8d97-431a-99f0-85c9524e118b\",\"code\":400,\"reason\":\"{\n \\\"error\\\": {\n \\\"errors\\\": [\n {\n \\\"domain\\\": \\\"global\\\",\n \\\"reason\\\": \\\"push.webhookUrlNotHttps\\\",\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n ],\n \\\"code\\\": 400,\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n}\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.WARNING: [Calendar] Sync failed {\"calendarId\":\"a33076c1-8d97-431a-99f0-85c9524e118b\",\"code\":400,\"reason\":\"{\n \\\"error\\\": {\n \\\"errors\\\": [\n {\n \\\"domain\\\": \\\"global\\\",\n \\\"reason\\\": \\\"push.webhookUrlNotHttps\\\",\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n ],\n \\\"code\\\": 400,\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n}\"} {\"correlation_id\":\"e95f214c-a38a-48ff-a8b5-6586e5429286\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [Calendar] Processing sync {\"calendarId\":\"2676cb6d-f86c-427e-bf78-591e388e3c1e\",\"from\":null,\"to\":null,\"delta\":\"CJ_x49O3jpIDEJ_x49O3jpIDGAUgw67KlwMow67KlwM=\",\"last_sync\":\"2026-01-19 07:48:40\",\"dateMode\":\"daily\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.WARNING: [Pipedrive] Account not connected for user {\"userId\":\"e6538737-e7b4-455f-a37a-3e79b665a220\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":1116,\"sociable_id\":241,\"provider_user_id\":\"19555731\",\"expires\":1779091997,\"refresh_token_expires\":null,\"provider\":\"pipedrive\",\"state\":\"full-refresh\",\"auth_scope\":\"base,deals:full,activities:full,contacts:full,search:read\",\"retry_after\":null,\"created_at\":\"2023-09-08 09:44:29\",\"updated_at\":\"2026-05-18 08:13:32\"}}} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"pipedrive\",\"crm_owner\":241,\"team_id\":19} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"pipedrive\",\"team_id\":19} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"pipedrive\",\"team_id\":19} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.WARNING: [Calendar] CRM disconnected for user so events will not be matched {\"provider\":\"pipedrive\",\"user_id\":241,\"message\":\"Your Pipedrive account has become disconnected. Please login to Jiminny to reconnect.\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1115,\"provider\":\"google\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [Google Calendar] Failed to watch channel for calendar {\"calendarId\":\"2676cb6d-f86c-427e-bf78-591e388e3c1e\",\"code\":400,\"reason\":\"{\n \\\"error\\\": {\n \\\"errors\\\": [\n {\n \\\"domain\\\": \\\"global\\\",\n \\\"reason\\\": \\\"push.webhookUrlNotHttps\\\",\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n ],\n \\\"code\\\": 400,\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n}\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.WARNING: [Calendar] Sync failed {\"calendarId\":\"2676cb6d-f86c-427e-bf78-591e388e3c1e\",\"code\":400,\"reason\":\"{\n \\\"error\\\": {\n \\\"errors\\\": [\n {\n \\\"domain\\\": \\\"global\\\",\n \\\"reason\\\": \\\"push.webhookUrlNotHttps\\\",\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n ],\n \\\"code\\\": 400,\n \\\"message\\\": \\\"WebHook callback must be HTTPS: /webhook/calendar/google?resourceType=event\\\"\n }\n}\"} {\"correlation_id\":\"ecee7320-c24d-41cb-ba7e-d6bccb02cba0\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1421,\"provider\":\"office\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1421,\"provider\":\"office\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [Calendar] Processing sync {\"calendarId\":\"9e8b1a2c-1a8f-42bd-b161-810fc0baf540\",\"from\":null,\"to\":null,\"delta\":\"R0usmcdvmMuZCBYV0hguCLbinXNUdndbb-ECUsN4LJrJG3mny6y_meJS8MAAHR5ZMrcUO3h48ocZNNnE7oP5gw1vlpVCP8G7vwKiJvMr__pTveeFSIgIX26vx0F0cDUmNI0_JTiUjIa8SS87NYFu0VC-K9UUoS_3YAmb3QbL1FzAfSGSNtasPG9UoNDP2sTOfaa0y-bXW7hGxDeemyMi5Q.mq5epx6DKMR2Kr4O2JnKftifGVfsgrCAgaghMEclE4U\",\"last_sync\":\"2026-05-27 06:58:28\",\"dateMode\":\"daily\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":89,\"team_id\":2} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}\n[2026-05-27 12:58:25] local.INFO: [MS Office Calendar] Skipping delta sync for daily mode {\"calendarId\":\"9e8b1a2c-1a8f-42bd-b161-810fc0baf540\"} {\"correlation_id\":\"f1c9b413-6e5b-4c12-af21-99b5174b2c5d\",\"trace_id\":\"bd5ca695-f258-458c-aba7-b0d201d21b51\"}","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}]...
|
-2918404396419486790
|
7358960207514864063
|
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;
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
10
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 12:57:12] local.NOTICE: Monitoring start {"correlation_id":"01b85e02-17ff-44f0-8d6e-484a810b1085","trace_id":"b4d68a19-67e7-41f9-a2f3-ca98f84e617b"}
[2026-05-27 12:57:12] local.NOTICE: Monitoring end {"correlation_id":"01b85e02-17ff-44f0-8d6e-484a810b1085","trace_id":"b4d68a19-67e7-41f9-a2f3-ca98f84e617b"}
[2026-05-27 12:57: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":"829043ea-15e1-40fe-a7ce-27fc8440984e","trace_id":"bd9f99cc-933a-453b-b9ff-869d04e9b49d"}
[2026-05-27 12:57: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":"829043ea-15e1-40fe-a7ce-27fc8440984e","trace_id":"bd9f99cc-933a-453b-b9ff-869d04e9b49d"}
[2026-05-27 12:57: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":"eb849911-4661-4753-b85d-3bd9dd3b0b9d","trace_id":"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a"}
[2026-05-27 12:57:15] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"eb849911-4661-4753-b85d-3bd9dd3b0b9d","trace_id":"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a"}
[2026-05-27 12:57:15] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"eb849911-4661-4753-b85d-3bd9dd3b0b9d","trace_id":"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a"}
[2026-05-27 12:57: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":"eb849911-4661-4753-b85d-3bd9dd3b0b9d","trace_id":"8508dc2d-48c1-4bc4-a89b-10e5cbd86d2a"}
[2026-05-27 12:57:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:create","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"1743fc5e-a278-45c5-989d-06afb0bfe24e","trace_id":"872b746a-3e40-49a0-b858-c4265fb9ebf1"}
[2026-05-27 12:57:19] local.INFO: [EmailSchedule] STARTING batch create {"host":"docker_lamp_1"} {"correlation_id":"1743fc5e-a278-45c5-989d-06afb0bfe24e","trace_id":"872b746a-3e40-49a0-b858-c4265fb9ebf1"}
[2026-05-27 12:57:19] local.INFO: [EmailSchedule] FINISHED batch create {"host":"docker_lamp_1"} {"correlation_id":"1743fc5e-a278-45c5-989d-06afb0bfe24e","trace_id":"872b746a-3e40-49a0-b858-c4265fb9ebf1"}
[2026-05-27 12:57:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:batch:create","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"1743fc5e-a278-45c5-989d-06afb0bfe24e","trace_id":"872b746a-3e40-49a0-b858-c4265fb9ebf1"}
[2026-05-27 12:57:21] local.INFO: [Jiminny\Jobs\Mailbox\CreateBatches] processed 1 inboxes and created 0 batches {"userId":null,"batchSize":30,"maxBatches":1000} {"correlation_id":"3f9ffe79-f6b7-483d-b4e2-253da2eb120e","trace_id":"872b746a-3e40-49a0-b858-c4265fb9ebf1"}
[2026-05-27 12:57:24] 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":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
[2026-05-27 12:57:24] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
[2026-05-27 12:57:25] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
[2026-05-27 12:57:25] 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":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
[2026-05-27 12:58:05] 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":"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b","trace_id":"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743"}
[2026-05-27 12:58:05] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b","trace_id":"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743"}
[2026-05-27 12:58:05] 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":"7c1c4fd4-05fb-487e-bf3a-7e6aeec8362b","trace_id":"ed9dd4c2-0f01-4f46-aaae-d5a9ce425743"}
[2026-05-27 12:58:07] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"90309e61-fa7c-4b5f-9c02-ce974df47319","trace_id":"26186ec1-112c-4099-a7ca-00105f5a4e64"}
[2026-05-27 12:58:07] 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":"90309e61-fa7c-4b5f-9c02-ce974df47319","trace_id":"26186ec1-112c-4099-a7ca-00105f5a4e64"}
[2026-05-27 12:58:08] local.NOTICE: Monitoring start {"correlation_id":"39cf2e27-b157-4709-b9e2-635c7ddbbd98","trace_id":"c4377397-a3de-4f5d-a07f-e92c1b0ecb7d"}
[2026-05-27 12:58:08] local.NOTICE: Monitoring end {"correlation_id":"39cf2e27-b157-4709-b9e2-635c7ddbbd98","trace_id":"c4377397-a3de-4f5d-a07f-e92c1b0ecb7d"}
[2026-05-27 12:58:11] 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":"50cef6f9-cbe0-4cbe-b1e9-68bc9ed550eb","trace_id":"c3442d24-9553-4395-a3b8-98ce1846f584"}
[2026-05-27 12:58:11] 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":"50cef6f9-cbe0-4cbe-b1e9-68bc9ed550eb","trace_id":"c3442d24-9553-4395-a3b8-98ce1846f584"}
[2026-05-27 12:58:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"f6c5ca29-2b41-4030-a550-1335fe2c4a11","trace_id":"582ee47c-7c2a-41dc-b454-b90e9f41af1a"}
[2026-05-27 12:58:13] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"f6c5ca29-2b41-4030-a550-1335fe2c4a11","trace_id":"582ee47c-7c2a-41dc-b454-b90e9f41af1a"}
[2026-05-27 12:58:13] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"f6c5ca29-2b41-4030-a550-1335fe2c4a11","trace_id":"582ee47c-7c2a-41dc-b454-b90e9f41af1a"}
[2026-05-27 12:58:13] 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":"f6c5ca29-2b41-4030-a550-1335fe2c4a11","trace_id":"582ee47c-7c2a-41dc-b454-b90e9f41af1a"}
[2026-05-27 12:58:14] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"abd5beea-c617-4fef-b30f-87b538e08cfa","trace_id":"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905"}
[2026-05-27 12:58:14] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:56:00, 2026-05-27 12:58:00] {"correlation_id":"abd5beea-c617-4fef-b30f-87b538e08cfa","trace_id":"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905"}
[2026-05-27 12:58:14] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:56:00, 2026-05-27 12:58:00] {"correlation_id":"abd5beea-c617-4fef-b30f-87b538e08cfa","trace_id":"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905"}
[2026-05-27 12:58:14] 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":"abd5beea-c617-4fef-b30f-87b538e08cfa","trace_id":"5a577ae2-bad7-4cdc-bee7-5e70ce7cb905"}
[2026-05-27 12:58:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"calendar:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:retry-failed","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"da72e8da-2f56-4b20-bfa7-d5396d6dc5d1","trace_id":"2328e196-0c82-430d-9d17-7b14a2a06770"}
[2026-05-27 12:58:18] local.NOTICE: Calendar sync start {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:batch:retry-failed","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"da72e8da-2f56-4b20-bfa7-d5396d6dc5d1","trace_id":"2328e196-0c82-430d-9d17-7b14a2a06770"}
[2026-05-27 12:58:18] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1393,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:18] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1393,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1393,"provider":"google","refreshToken":"5aa7e2d96b53201cd16fca5d2e4ef3ad03320971fc064781d18aee3ae7b99fbf","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1393,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Account has been deleted"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1393,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1387,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1387,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1387,"provider":"google","refreshToken":"8157ac6de94842937194009e9c50e459253600f799dacf6a40755ffdbeb5bba6","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1387,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Account has been deleted"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1387,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1348,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1348,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:19] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1348,"provider":"google","refreshToken":"9e7d13d3032d0cb1b79d8e95aef01383e8e91eb52ff8ee960c8a0b6b95cd8c73","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1348,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Bad Request"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1348,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1361,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1361,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1361,"provider":"google","refreshToken":"6c843da199c2b9907445329304fcc4ec5057a4ee748d8299641764395c08e1fd","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1361,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Account has been deleted"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1361,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1310,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1310,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1310,"provider":"google","refreshToken":"e34818922c2830a660813a63f6169a4a9a992ae2cccd7dc8dd7796cfdb470ef1","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1310,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Bad Request"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1310,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1333,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1333,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1333,"provider":"google","refreshToken":"6c902986546d8e8da1dc539b046cdc1d458f519acc972e5b5f1d6a1a295165e0","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1333,"provider":"google","responseBody":{"error":"unauthorized_client","error_description":"Unauthorized"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1333,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1368,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1368,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1368,"provider":"google","refreshToken":"d2f128898ff8543bd16b69cfae37896ab85119b0f5ed2b431d739593bb600333","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1368,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Bad Request"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1368,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1365,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1365,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1365,"provider":"google","refreshToken":"7676e4a9afcd082b413248ab5ec6e487021fec6a9bdf315860a59cefad9caad8","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1365,"provider":"google","responseBody":{"error":"unauthorized_client","error_description":"Unauthorized"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1365,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1364,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1364,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:20] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1364,"provider":"google","refreshToken":"dd5882ebce76e645292ce33ae74238abbb77c0a4ecc6a2bfe723cad82e72ba8e","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1364,"provider":"google","responseBody":{"error":"unauthorized_client","error_description":"Unauthorized"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1364,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1370,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1370,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1370,"provider":"office","refreshToken":"b7ee8035306d0043cea6e00e7c4fe14f745e44074a1194db62a31cdf8b70af3e","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1370,"provider":"office","responseBody":"{\"error\":\"invalid_client\",\"error_description\":\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: e8f91f16-b4dc-40e7-985c-d4a3ac516000 Correlation ID: 0a8d37fe-9d50-4b4c-a1ae-77e0327f15fc Timestamp: 2026-05-27 12:58:21Z\",\"error_codes\":[7000215],\"timestamp\":\"2026-05-27 12:58:21Z\",\"trace_id\":\"e8f91f16-b4dc-40e7-985c-d4a3ac516000\",\"correlation_id\":\"0a8d37fe-9d50-4b4c-a1ae-77e0327f15fc\",\"error_uri\":\"https://login.microsoftonline.com/error?code=7000215\"}"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1370,"provider":"office","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1202,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1202,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:21] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1202,"provider":"office","refreshToken":"b458799ccc29b21a6e2eb5260fdb63e49ccba21bf942a3973fb63799bd7f0afe","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1202,"provider":"office","responseBody":"{\"error\":\"invalid_client\",\"error_description\":\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: 3e9be67a-9c49-4a40-ad26-4af7bae65000 Correlation ID: 7fb2008c-07fe-4705-a6ff-47e70f64b2bb Timestamp: 2026-05-27 12:58:22Z\",\"error_codes\":[7000215],\"timestamp\":\"2026-05-27 12:58:22Z\",\"trace_id\":\"3e9be67a-9c49-4a40-ad26-4af7bae65000\",\"correlation_id\":\"7fb2008c-07fe-4705-a6ff-47e70f64b2bb\",\"error_uri\":\"https://login.microsoftonline.com/error?code=7000215\"}"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1202,"provider":"office","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1502,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1502,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: Calendar sync job dispatched {"calendar_id":501} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1300,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1300,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1300,"provider":"google","refreshToken":"4b811db0725fd9602a95943519a7da935e2a5065da7d9ebfcb170752e3e1ddb8","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1300,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Account has been deleted"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1300,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1409,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1409,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1409,"provider":"google","refreshToken":"e2a3f2d06894894eed1ee87d9db1ace77d4d42ee6e1288a8940ad2c10333b0c4","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1409,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Bad Request"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1409,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1352,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1352,"provider":"google"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1352,"provider":"google","refreshToken":"dd4b16b00fdc1216da6b717c02338c073636e29162826b2de6db3f064fc029eb","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1352,"provider":"google","responseBody":{"error":"invalid_grant","error_description":"Bad Request"}} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1352,"provider":"google","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1296,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1296,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:22] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1296,"provider":"office","refreshToken":"011ae723c9d800c674e0b4be76f49fc046dac7d501b66c59ef0d9549cfa56ae5","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1296,"provider":"office","responseBody":"{\"error\":\"invalid_client\",\"error_description\":\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: cbdfd43b-d682-4397-8a35-69af29c81300 Correlation ID: 723e1e62-f593-454d-b972-845d22c92271 Timestamp: 2026-05-27 12:58:23Z\",\"error_codes\":[7000215],\"timestamp\":\"2026-05-27 12:58:23Z\",\"trace_id\":\"cbdfd43b-d682-4397-8a35-69af29c81300\",\"correlation_id\":\"723e1e62-f593-454d-b972-845d22c92271\",\"error_uri\":\"https://login.microsoftonline.com/error?code=7000215\"}"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":1296,"provider":"office","reason":"Flow refresh required."} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":391,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":391,"provider":"office"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":391,"provider":"office","refreshToken":"00045eebae0f39b34887c6d53f92ae78064f7145e1f4b67754aebd03cfb2d881","state":"full-refresh"} {"correlation_id":"b939891c-4c61-4fc3-b7d5-4bcf72084fce","trace_id":"bd5ca695-f258-458c-aba7-b0d201d21b51"}
[2026-05-27 12:58:23] local.ERROR: [SocialAccountService] Failed to refresh token {"socialAccountId":391,"provider":"office","responseBody":"{\"error\":\"invalid_client\",\"error_description\":\"AADSTS7000215: Invalid client secret provided. Ensure the secret being sent in the request is the client secret value, not the client secret ID, for a secret added to app 'bbcbb2ef-6200-4fae-82bd-d81f5dd738da'. Trace ID: f559a2e0-ad9d-4163-bd2c-dcf7c33f1200 Correlation ID: 44266705-aff3-4701-aa5e-a88b7867593a Timestamp: 2026-05-27 12:58:23Z\",\"error_codes\":[7000215],\"timestamp\":\"2026-05-27 12:58:23Z\",\"trace_id\":\"f559a2e0-ad9d-4163...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78760
|
2763
|
43
|
2026-05-27T12:58:42.825399+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886722825_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
|
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...
|
78759
|
NULL
|
NULL
|
NULL
|
|
78758
|
2763
|
41
|
2026-05-27T12:58:38.402391+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886718402_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...
|
[{"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}]...
|
6445247769588009110
|
-6618793407836651056
|
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
iTerm2ShellEditViewSessionScriptsProfilesWindowHelp100% <DEV (docker)screenpipe"DOCKER₴81DEV (docker)$2-zsh883jiminny-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: startedroot@docker_lamp_1:/home/jiminny# php artisan mailbox: text-relay: syncRelayed 0 message(s).root@docker_lamp_1:/home/jiminny# php artisan optimize:clear && supervisorctl restart allINFOClearing cached bootstrap files.configcachecompiledeventsroutesviewsjiminny-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: stopped84-zsh*5...ip-10-30-129-190:~ (nc)₴86Wed 27 May 15:58:37T81•..-10-30-140-255:~ (-zsh)$7DEV2.50ms DONE9.59ms DONE1.39ms DONE1.06ms DONE1.07ms DONE4.84ms DONE...
|
78756
|
NULL
|
NULL
|
NULL
|
|
78753
|
2764
|
45
|
2026-05-27T12:58:25.988143+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886705988_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
16
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;
}
$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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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 \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 \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":true,"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":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"value":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"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}]...
|
816333390822493230
|
-5281383083183535733
|
typing_pause
|
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
16
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;
}
$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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78752
|
NULL
|
NULL
|
NULL
|
|
78752
|
2764
|
44
|
2026-05-27T12:58:24.913502+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886704913_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
16
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;
}
$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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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":true,"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":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"value":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"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}]...
|
8911248186958597539
|
-5281383083183535733
|
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
16
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;
}
$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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78751
|
2764
|
43
|
2026-05-27T12:58:21.351421+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886701351_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
16
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;
}
$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...
|
[{"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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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":true,"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}]...
|
-1837709289335154242
|
-6438807707446769269
|
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
16
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;
}
$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...
|
78750
|
NULL
|
NULL
|
NULL
|
|
78750
|
2764
|
42
|
2026-05-27T12:58:04.930157+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886684930_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
16
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;
}
$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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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":true,"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":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"value":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"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}]...
|
8911248186958597539
|
-5281383083183535733
|
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
16
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;
}
$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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78749
|
2763
|
38
|
2026-05-27T12:58:04.568362+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886684568_m1.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
16
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;
}
$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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
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":"16","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\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\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":true,"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 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","depth":4,"on_screen":true,"value":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"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}]...
|
8911248186958597539
|
-5281383083183535733
|
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
16
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;
}
$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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78748
|
2763
|
37
|
2026-05-27T12:58:03.729972+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886683729_m1.jpg...
|
PhpStorm
|
faVsco.js – SyncMailbox.php
|
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 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
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":true,"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 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","depth":4,"on_screen":true,"value":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"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}]...
|
-5036258594377021821
|
-1317633669619957426
|
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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78747
|
NULL
|
NULL
|
NULL
|
|
78747
|
2763
|
36
|
2026-05-27T12:58:02.630610+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886682630_m1.jpg...
|
PhpStorm
|
faVsco.js – SyncMailbox.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp•DOCKEREventsRoutesViews•₴1DEV (docker)₴2-zsh883DriversBroadcastingCacheDatabaseLogsMailQueueSessionStoragepublic/storageSentryEnabledEnvironmentLaravel SDK VersionPHP SDK VersionReleaseSample Rate ErrorsSample Rate Performance MonitoringSample Rate ProfilingSend Default PIIroot@ip-10-30-135-89:/home/jiminny# php artisantinkerPsy Shell v0.12.22 (PHP 8.5.5 - cli) by Justin HilemanNew PHP|manual is available (latest: 3.0.7). Update with'doc--update-manual> Sc = \Cache::get(config('jiminny.google_text_relay_topic'));"358684exitINFOGoodbye.root@ip-10-30-135-89:/home/jiminny# exitexit[ec2-user@ip-10-30-140-255 ~]$ exitlogoutConnection to jiminny-stage-ecs1 closed.lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $|100% С8• Wed 27 May 15:58:02181ec2-user@ip-10-30-140-255:~screenpipe"84-zsh85...ip-10-30-129-190:~ (nc)NOT CACHEDCACHEDCACHED₴86...-10-30-140-255:~ (-zsh)pusherredismysqlerrorlogsessqsredisNOT LINKEDYESstaging4.13.04.13.0893709100%NOT SETNOT SETDISABLED...
|
NULL
|
7935047559346439857
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp•DOCKEREventsRoutesViews•₴1DEV (docker)₴2-zsh883DriversBroadcastingCacheDatabaseLogsMailQueueSessionStoragepublic/storageSentryEnabledEnvironmentLaravel SDK VersionPHP SDK VersionReleaseSample Rate ErrorsSample Rate Performance MonitoringSample Rate ProfilingSend Default PIIroot@ip-10-30-135-89:/home/jiminny# php artisantinkerPsy Shell v0.12.22 (PHP 8.5.5 - cli) by Justin HilemanNew PHP|manual is available (latest: 3.0.7). Update with'doc--update-manual> Sc = \Cache::get(config('jiminny.google_text_relay_topic'));"358684exitINFOGoodbye.root@ip-10-30-135-89:/home/jiminny# exitexit[ec2-user@ip-10-30-140-255 ~]$ exitlogoutConnection to jiminny-stage-ecs1 closed.lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $|100% С8• Wed 27 May 15:58:02181ec2-user@ip-10-30-140-255:~screenpipe"84-zsh85...ip-10-30-129-190:~ (nc)NOT CACHEDCACHEDCACHED₴86...-10-30-140-255:~ (-zsh)pusherredismysqlerrorlogsessqsredisNOT LINKEDYESstaging4.13.04.13.0893709100%NOT SETNOT SETDISABLED...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78744
|
2764
|
40
|
2026-05-27T12:57:33.713234+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886653713_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
16
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;
}
$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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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":true,"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":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"value":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"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}]...
|
8911248186958597539
|
-5281383083183535733
|
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
16
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;
}
$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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78743
|
2764
|
39
|
2026-05-27T12:57:29.682202+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886649682_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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
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":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"value":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","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}]...
|
-5036258594377021821
|
-1317633669619957426
|
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 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78739
|
NULL
|
NULL
|
NULL
|
|
78742
|
2763
|
34
|
2026-05-27T12:57:29.253940+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886649253_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 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
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 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","depth":4,"on_screen":true,"value":"[2026-05-27 12:57:25] local.INFO: $messageHistory \nArray\n(\n)\n {\"correlation_id\":\"e4286517-4279-4047-8135-b79adc851022\",\"trace_id\":\"0470f04d-52ab-4a34-9f7e-7bb500016954\"}","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}]...
|
-5036258594377021821
|
-1317633669619957426
|
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
[2026-05-27 12:57:25] local.INFO: $messageHistory
Array
(
)
{"correlation_id":"e4286517-4279-4047-8135-b79adc851022","trace_id":"0470f04d-52ab-4a34-9f7e-7bb500016954"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78738
|
2764
|
37
|
2026-05-27T12:57:18.546753+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886638546_m2.jpg...
|
PhpStorm
|
faVsco.js – SyncMailbox.php
|
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":true,"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);
}
}
}...
|
78736
|
NULL
|
NULL
|
NULL
|
|
78734
|
2764
|
34
|
2026-05-27T12:57:10.901287+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886630901_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.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
16
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;
}
$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
122
Previous Highlighted Error
Next Highlighted Error
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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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.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":"122","depth":4,"bounds":{"left":0.7257314,"top":0.10055866,"width":0.011635638,"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":"","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"value":"","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}]...
|
3162128085625244858
|
-5281383151892526197
|
typing_pause
|
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
16
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;
}
$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
122
Previous Highlighted Error
Next Highlighted Error
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78733
|
2764
|
33
|
2026-05-27T12:57:07.635492+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886627635_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.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
16
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;
}
$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
118
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:14] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:14] 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":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring start {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring end {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] 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":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:20] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55:20] 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":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55: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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] 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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {"correlation_id":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] 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":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:31] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesEnded {"from":"12:50","to":"12:55"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {"from":"02:45","to":"02:50"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] 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":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":59,"provider":"hubspot","refreshToken":"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":306,"provider":"hubspot","refreshToken":"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1372,"provider":"hubspot","refreshToken":"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {"total":3,"fixed":0,"failed":3} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {"memory_limit":"256M","max_execution_time":"0","initial_memory_mb":62.0} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {"expires_at":"2026-05-27T12:57:45.940375Z"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {"expires_in":1800,"cached_for":1500} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:05] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:05] 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":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring start {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring end {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] 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":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:11] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] 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":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","usage":24777784,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1499,"provider":"hubspot","refreshToken":"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {"socialAccountId":1499,"provider":"hubspot","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {"team":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {"team_id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/deals/search","total_requests":1,"total_records_fetched":0,"total_elapsed_seconds":0.28,"average_seconds_per_request":0.28} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {"team":2,"strategies":"lastModified","sync_count":0,"total":0,"last_synced_id":null,"duration_ms":286.56} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","provider":"hubspot","status":"completed","duration_ms":1417.45,"usage":25188000,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","usage":25162720,"real_usage":65011712,"pid":42210} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"33e34a7a-1c02-4f04-87ac-22c3a385e6e3","account":{"Jiminny\\Models\\SocialAccount":{"id":306,"sociable_id":109,"provider_user_id":"11348452","expires":1701077403,"refresh_token_expires":null,"provider":"hubspot","state":"full-refresh","auth_scope":null,"retry_after":null,"created_at":"2020-09-01 16:59:04","updated_at":"2023-11-27 09:30:03"}}} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":109,"team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","provider":"hubspot","status":"disconnected","duration_ms":28.83,"usage":25162928,"real_usage":65011712,"pid":42210,"reason":"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect."} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","usage":25120440,"real_usage":65011712,"pid":42210} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b","account":null} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":130,"team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","provider":"hubspot","status":"disconnected","duration_ms":50.47,"usage":25140104,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","usage":25100792,"real_usage":65011712,"pid":42210} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"71e3aac5-fb66-47c5-a236-2d051ae3e319","account":null} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":256,"team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","provider":"hubspot","status":"disconnected","duration_ms":61.91,"usage":25136552,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {"host":"docker_lamp_1"} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {"host":"docker_lamp_1","events":2} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {"userId":"641f1acb-16b8-42d1-8726-df52979dad0e","account":{"Jiminny\\Models\\SocialAccount":{"id":1500,"sociable_id":143,"provider_user_id":"0052g000003frelAAA","expires":null,"refresh_token_expires":null,"provider":"salesforce","state":"full-refresh","auth_scope":"refresh_token web api","retry_after":null,"created_at":"2026-02-06 08:39:03","updated_at":"2026-05-14 09:46:30"}}} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"salesforce","crm_owner":143,"team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.WARNING: [HubSpot Journal Polling] Maximum empty results reached, stopping {"empty_results":5,"max_empty_results":5} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.WARNING: [HubSpot Journal Polling] Maximum empty results reached, stopping {"empty_results":5,"max_empty_results":5} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Service ending {"runtime_seconds":57,"total_cycles":5,"files_downloaded":0,"empty_files":0,"other_portal_skipped":0,"total_events":0,"events_per_file":0,"avg_api_ms":250.7,"avg_download_ms":0.0,"avg_transform_ms":0.0,"avg_process_ms":0.0,"peak_memory_mb":99.88} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Released polling lock {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:58] local.INFO: [Commands/AsyncUpdateEsEntities] Starting ES update worker {"pid":50064,"target":"activities"} {"correlation_id":"f12c12af-7383-420f-b547-0ed048bf10da","trace_id":"3ff9040b-43e3-4602-932f-30859690fe64"}
[2026-05-27 12:57:03] 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":"e638e561-7424-43cb-bc2e-fa565d3f9752","trace_id":"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e"}
[2026-05-27 12:57:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"e638e561-7424-43cb-bc2e-fa565d3f9752","trace_id":"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e"}
[2026-05-27 12:57:03] 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":"e638e561-7424-43cb-bc2e-fa565d3f9752","trace_id":"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e"}
[2026-05-27 12:57:06] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"a4814a94-a77d-4ba9-9c82-567ae915042d","trace_id":"ec875a7f-5c74-427b-a1a1-320b92486ebe"}
[2026-05-27 12:57:06] 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":"a4814a94-a77d-4ba9-9c82-567ae915042d","trace_id":"ec875a7f-5c74-427b-a1a1-320b92486ebe"}...
|
[{"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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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.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":"118","depth":4,"bounds":{"left":0.72606385,"top":0.10055866,"width":0.011303191,"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 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:14] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:14] 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\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring start {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring end {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] 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\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:20] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55:20] 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\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55: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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] 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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] 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\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"12:50\",\"to\":\"12:55\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"02:45\",\"to\":\"02:50\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] 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\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":62.0} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-27T12:57:45.940375Z\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:05] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:05] 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\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring start {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring end {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] 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\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:11] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] 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\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"usage\":24777784,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"refreshToken\":\"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {\"team\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.28,\"average_seconds_per_request\":0.28} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {\"team\":2,\"strategies\":\"lastModified\",\"sync_count\":0,\"total\":0,\"last_synced_id\":null,\"duration_ms\":286.56} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"provider\":\"hubspot\",\"status\":\"completed\",\"duration_ms\":1417.45,\"usage\":25188000,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"usage\":25162720,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"33e34a7a-1c02-4f04-87ac-22c3a385e6e3\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":306,\"sociable_id\":109,\"provider_user_id\":\"11348452\",\"expires\":1701077403,\"refresh_token_expires\":null,\"provider\":\"hubspot\",\"state\":\"full-refresh\",\"auth_scope\":null,\"retry_after\":null,\"created_at\":\"2020-09-01 16:59:04\",\"updated_at\":\"2023-11-27 09:30:03\"}}} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":109,\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":28.83,\"usage\":25162928,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect.\"} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"usage\":25120440,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b\",\"account\":null} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":130,\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":50.47,\"usage\":25140104,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"usage\":25100792,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"71e3aac5-fb66-47c5-a236-2d051ae3e319\",\"account\":null} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":256,\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":61.91,\"usage\":25136552,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {\"host\":\"docker_lamp_1\",\"events\":2} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {\"userId\":\"641f1acb-16b8-42d1-8726-df52979dad0e\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":1500,\"sociable_id\":143,\"provider_user_id\":\"0052g000003frelAAA\",\"expires\":null,\"refresh_token_expires\":null,\"provider\":\"salesforce\",\"state\":\"full-refresh\",\"auth_scope\":\"refresh_token web api\",\"retry_after\":null,\"created_at\":\"2026-02-06 08:39:03\",\"updated_at\":\"2026-05-14 09:46:30\"}}} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"salesforce\",\"crm_owner\":143,\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.WARNING: [HubSpot Journal Polling] Maximum empty results reached, stopping {\"empty_results\":5,\"max_empty_results\":5} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.WARNING: [HubSpot Journal Polling] Maximum empty results reached, stopping {\"empty_results\":5,\"max_empty_results\":5} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Service ending {\"runtime_seconds\":57,\"total_cycles\":5,\"files_downloaded\":0,\"empty_files\":0,\"other_portal_skipped\":0,\"total_events\":0,\"events_per_file\":0,\"avg_api_ms\":250.7,\"avg_download_ms\":0.0,\"avg_transform_ms\":0.0,\"avg_process_ms\":0.0,\"peak_memory_mb\":99.88} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Released polling lock {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:58] local.INFO: [Commands/AsyncUpdateEsEntities] Starting ES update worker {\"pid\":50064,\"target\":\"activities\"} {\"correlation_id\":\"f12c12af-7383-420f-b547-0ed048bf10da\",\"trace_id\":\"3ff9040b-43e3-4602-932f-30859690fe64\"}\n[2026-05-27 12:57:03] 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\":\"e638e561-7424-43cb-bc2e-fa565d3f9752\",\"trace_id\":\"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e\"}\n[2026-05-27 12:57:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"e638e561-7424-43cb-bc2e-fa565d3f9752\",\"trace_id\":\"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e\"}\n[2026-05-27 12:57:03] 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\":\"e638e561-7424-43cb-bc2e-fa565d3f9752\",\"trace_id\":\"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e\"}\n[2026-05-27 12:57:06] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"a4814a94-a77d-4ba9-9c82-567ae915042d\",\"trace_id\":\"ec875a7f-5c74-427b-a1a1-320b92486ebe\"}\n[2026-05-27 12:57:06] 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\":\"a4814a94-a77d-4ba9-9c82-567ae915042d\",\"trace_id\":\"ec875a7f-5c74-427b-a1a1-320b92486ebe\"}","depth":4,"bounds":{"left":0.4331782,"top":0.09736632,"width":0.5668218,"height":0.90263367},"on_screen":true,"value":"[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:14] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:14] 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\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring start {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring end {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] 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\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:20] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55:20] 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\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55: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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] 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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] 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\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"12:50\",\"to\":\"12:55\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"02:45\",\"to\":\"02:50\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] 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\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":62.0} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-27T12:57:45.940375Z\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:05] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:05] 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\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring start {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring end {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] 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\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:11] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] 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\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"usage\":24777784,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"refreshToken\":\"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {\"team\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.28,\"average_seconds_per_request\":0.28} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {\"team\":2,\"strategies\":\"lastModified\",\"sync_count\":0,\"total\":0,\"last_synced_id\":null,\"duration_ms\":286.56} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"provider\":\"hubspot\",\"status\":\"completed\",\"duration_ms\":1417.45,\"usage\":25188000,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"usage\":25162720,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"33e34a7a-1c02-4f04-87ac-22c3a385e6e3\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":306,\"sociable_id\":109,\"provider_user_id\":\"11348452\",\"expires\":1701077403,\"refresh_token_expires\":null,\"provider\":\"hubspot\",\"state\":\"full-refresh\",\"auth_scope\":null,\"retry_after\":null,\"created_at\":\"2020-09-01 16:59:04\",\"updated_at\":\"2023-11-27 09:30:03\"}}} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":109,\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":28.83,\"usage\":25162928,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect.\"} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"usage\":25120440,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b\",\"account\":null} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":130,\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":50.47,\"usage\":25140104,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"usage\":25100792,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"71e3aac5-fb66-47c5-a236-2d051ae3e319\",\"account\":null} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":256,\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":61.91,\"usage\":25136552,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {\"host\":\"docker_lamp_1\",\"events\":2} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {\"userId\":\"641f1acb-16b8-42d1-8726-df52979dad0e\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":1500,\"sociable_id\":143,\"provider_user_id\":\"0052g000003frelAAA\",\"expires\":null,\"refresh_token_expires\":null,\"provider\":\"salesforce\",\"state\":\"full-refresh\",\"auth_scope\":\"refresh_token web api\",\"retry_after\":null,\"created_at\":\"2026-02-06 08:39:03\",\"updated_at\":\"2026-05-14 09:46:30\"}}} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"salesforce\",\"crm_owner\":143,\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.WARNING: [HubSpot Journal Polling] Maximum empty results reached, stopping {\"empty_results\":5,\"max_empty_results\":5} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.WARNING: [HubSpot Journal Polling] Maximum empty results reached, stopping {\"empty_results\":5,\"max_empty_results\":5} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Service ending {\"runtime_seconds\":57,\"total_cycles\":5,\"files_downloaded\":0,\"empty_files\":0,\"other_portal_skipped\":0,\"total_events\":0,\"events_per_file\":0,\"avg_api_ms\":250.7,\"avg_download_ms\":0.0,\"avg_transform_ms\":0.0,\"avg_process_ms\":0.0,\"peak_memory_mb\":99.88} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Released polling lock {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:58] local.INFO: [Commands/AsyncUpdateEsEntities] Starting ES update worker {\"pid\":50064,\"target\":\"activities\"} {\"correlation_id\":\"f12c12af-7383-420f-b547-0ed048bf10da\",\"trace_id\":\"3ff9040b-43e3-4602-932f-30859690fe64\"}\n[2026-05-27 12:57:03] 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\":\"e638e561-7424-43cb-bc2e-fa565d3f9752\",\"trace_id\":\"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e\"}\n[2026-05-27 12:57:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"e638e561-7424-43cb-bc2e-fa565d3f9752\",\"trace_id\":\"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e\"}\n[2026-05-27 12:57:03] 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\":\"e638e561-7424-43cb-bc2e-fa565d3f9752\",\"trace_id\":\"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e\"}\n[2026-05-27 12:57:06] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"a4814a94-a77d-4ba9-9c82-567ae915042d\",\"trace_id\":\"ec875a7f-5c74-427b-a1a1-320b92486ebe\"}\n[2026-05-27 12:57:06] 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\":\"a4814a94-a77d-4ba9-9c82-567ae915042d\",\"trace_id\":\"ec875a7f-5c74-427b-a1a1-320b92486ebe\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false}]...
|
119370622639584746
|
-2344704212223857347
|
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
16
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;
}
$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
118
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:14] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:14] 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":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring start {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring end {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] 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":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:20] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55:20] 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":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55: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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] 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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {"correlation_id":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] 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":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:31] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesEnded {"from":"12:50","to":"12:55"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {"from":"02:45","to":"02:50"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] 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":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":59,"provider":"hubspot","refreshToken":"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":306,"provider":"hubspot","refreshToken":"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1372,"provider":"hubspot","refreshToken":"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {"total":3,"fixed":0,"failed":3} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {"memory_limit":"256M","max_execution_time":"0","initial_memory_mb":62.0} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {"expires_at":"2026-05-27T12:57:45.940375Z"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {"expires_in":1800,"cached_for":1500} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:05] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:05] 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":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring start {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring end {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] 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":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:11] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] 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":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","usage":24777784,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1499,"provider":"hubspot","refreshToken":"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {"socialAccountId":1499,"provider":"hubspot","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {"team":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {"team_id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/deals/search","total_requests":1,"total_records_fetched":0,"total_elapsed_seconds":0.28,"average_seconds_per_request":0.28} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {"team":2,"strategies":"lastModified","sync_count":0,"total":0,"last_synced_id":null,"duration_ms":286.56} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","provider":"hubspot","status":"completed","duration_ms":1417.45,"usage":25188000,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","usage":25162720,"real_usage":65011712,"pid":42210} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"33e34a7a-1c02-4f04-87ac-22c3a385e6e3","account":{"Jiminny\\Models\\SocialAccount":{"id":306,"sociable_id":109,"provider_user_id":"11348452","expires":1701077403,"refresh_token_expires":null,"provider":"hubspot","state":"full-refresh","auth_scope":null,"retry_after":null,"created_at":"2020-09-01 16:59:04","updated_at":"2023-11-27 09:30:03"}}} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":109,"team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","provider":"hubspot","status":"disconnected","duration_ms":28.83,"usage":25162928,"real_usage":65011712,"pid":42210,"reason":"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect."} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","usage":25120440,"real_usage":65011712,"pid":42210} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b","account":null} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":130,"team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","provider":"hubspot","status":"disconnected","duration_ms":50.47,"usage":25140104,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","usage":25100792,"real_usage":65011712,"pid":42210} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"71e3aac5-fb66-47c5-a236-2d051ae3e319","account":null} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":256,"team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","provider":"hubspot","status":"disconnected","duration_ms":61.91,"usage":25136552,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {"host":"docker_lamp_1"} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {"host":"docker_lamp_1","events":2} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {"userId":"641f1acb-16b8-42d1-8726-df52979dad0e","account":{"Jiminny\\Models\\SocialAccount":{"id":1500,"sociable_id":143,"provider_user_id":"0052g000003frelAAA","expires":null,"refresh_token_expires":null,"provider":"salesforce","state":"full-refresh","auth_scope":"refresh_token web api","retry_after":null,"created_at":"2026-02-06 08:39:03","updated_at":"2026-05-14 09:46:30"}}} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"salesforce","crm_owner":143,"team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.WARNING: [HubSpot Journal Polling] Maximum empty results reached, stopping {"empty_results":5,"max_empty_results":5} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.WARNING: [HubSpot Journal Polling] Maximum empty results reached, stopping {"empty_results":5,"max_empty_results":5} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Service ending {"runtime_seconds":57,"total_cycles":5,"files_downloaded":0,"empty_files":0,"other_portal_skipped":0,"total_events":0,"events_per_file":0,"avg_api_ms":250.7,"avg_download_ms":0.0,"avg_transform_ms":0.0,"avg_process_ms":0.0,"peak_memory_mb":99.88} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:42] local.INFO: [HubSpot Journal Polling] Released polling lock {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:58] local.INFO: [Commands/AsyncUpdateEsEntities] Starting ES update worker {"pid":50064,"target":"activities"} {"correlation_id":"f12c12af-7383-420f-b547-0ed048bf10da","trace_id":"3ff9040b-43e3-4602-932f-30859690fe64"}
[2026-05-27 12:57:03] 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":"e638e561-7424-43cb-bc2e-fa565d3f9752","trace_id":"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e"}
[2026-05-27 12:57:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"e638e561-7424-43cb-bc2e-fa565d3f9752","trace_id":"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e"}
[2026-05-27 12:57:03] 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":"e638e561-7424-43cb-bc2e-fa565d3f9752","trace_id":"fb541cfd-fb6f-4f99-a3f8-069be37ccd5e"}
[2026-05-27 12:57:06] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"a4814a94-a77d-4ba9-9c82-567ae915042d","trace_id":"ec875a7f-5c74-427b-a1a1-320b92486ebe"}
[2026-05-27 12:57:06] 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":"a4814a94-a77d-4ba9-9c82-567ae915042d","trace_id":"ec875a7f-5c74-427b-a1a1-320b92486ebe"}...
|
78732
|
NULL
|
NULL
|
NULL
|
|
78732
|
2764
|
32
|
2026-05-27T12:57:06.514321+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886626514_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
16
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;
}
$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...
|
[{"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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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}]...
|
-7639128014968712822
|
-6438807707446769269
|
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
16
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;
}
$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...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78731
|
2763
|
30
|
2026-05-27T12:57:04.651341+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886624651_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.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...
|
[{"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}]...
|
-4060176888399009113
|
-9212323791225028208
|
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
iTerm2ShellEditViewSessionScriptsProfilesWindowHelp100% <DEV (docker)screenpipe"DOCKER* *81DEV (docker)$2-zshconfigcachecompiledeventsroutesviewsjiminny-worker-processing-delayed:jiminny-worker-processing-delayed_00:jiminny-worker-processing-2:jiminny-worker-processing-2_00:-25in -Seloyed 00: stoppestoppedjiminny-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-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00: stoppedworker-emails:worker-emails_00: stoppedworker-nudges:worker-nudges_00: stoppedworker:worker_00: stoppedworker-audio:worker-audio_00: stoppedworker-calendar:worker-calendar_00: stoppedworker-crm-sync:worker-crm-sync_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker-conferences:worker-conferences_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:startedroot@docker_lamp_1:/home/jiminny#O ₴4-zsh*5...ip-10-30-129-190:~ (nc)3.45ms DONE10.26ms DONE1.84ms DONE0.72ms DONE6.62ms DONE4.33ms DONE₴868• Wed 27 May 15:57:04T&1•..-10-30-140-255:~ (-zsh)$7DEV...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78726
|
2764
|
29
|
2026-05-27T12:56:36.892437+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886596892_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.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
16
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;
}
$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
108
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:14] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:14] 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":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring start {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring end {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] 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":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:20] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55:20] 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":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55: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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] 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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {"correlation_id":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] 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":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:31] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesEnded {"from":"12:50","to":"12:55"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {"from":"02:45","to":"02:50"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] 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":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":59,"provider":"hubspot","refreshToken":"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":306,"provider":"hubspot","refreshToken":"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1372,"provider":"hubspot","refreshToken":"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {"total":3,"fixed":0,"failed":3} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {"memory_limit":"256M","max_execution_time":"0","initial_memory_mb":62.0} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {"expires_at":"2026-05-27T12:57:45.940375Z"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {"expires_in":1800,"cached_for":1500} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:05] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:05] 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":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring start {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring end {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] 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":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:11] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] 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":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","usage":24777784,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1499,"provider":"hubspot","refreshToken":"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {"socialAccountId":1499,"provider":"hubspot","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {"team":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {"team_id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/deals/search","total_requests":1,"total_records_fetched":0,"total_elapsed_seconds":0.28,"average_seconds_per_request":0.28} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {"team":2,"strategies":"lastModified","sync_count":0,"total":0,"last_synced_id":null,"duration_ms":286.56} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","provider":"hubspot","status":"completed","duration_ms":1417.45,"usage":25188000,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","usage":25162720,"real_usage":65011712,"pid":42210} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"33e34a7a-1c02-4f04-87ac-22c3a385e6e3","account":{"Jiminny\\Models\\SocialAccount":{"id":306,"sociable_id":109,"provider_user_id":"11348452","expires":1701077403,"refresh_token_expires":null,"provider":"hubspot","state":"full-refresh","auth_scope":null,"retry_after":null,"created_at":"2020-09-01 16:59:04","updated_at":"2023-11-27 09:30:03"}}} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":109,"team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","provider":"hubspot","status":"disconnected","duration_ms":28.83,"usage":25162928,"real_usage":65011712,"pid":42210,"reason":"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect."} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","usage":25120440,"real_usage":65011712,"pid":42210} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b","account":null} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":130,"team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","provider":"hubspot","status":"disconnected","duration_ms":50.47,"usage":25140104,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","usage":25100792,"real_usage":65011712,"pid":42210} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"71e3aac5-fb66-47c5-a236-2d051ae3e319","account":null} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":256,"team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","provider":"hubspot","status":"disconnected","duration_ms":61.91,"usage":25136552,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {"host":"docker_lamp_1"} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {"host":"docker_lamp_1","events":2} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {"userId":"641f1acb-16b8-42d1-8726-df52979dad0e","account":{"Jiminny\\Models\\SocialAccount":{"id":1500,"sociable_id":143,"provider_user_id":"0052g000003frelAAA","expires":null,"refresh_token_expires":null,"provider":"salesforce","state":"full-refresh","auth_scope":"refresh_token web api","retry_after":null,"created_at":"2026-02-06 08:39:03","updated_at":"2026-05-14 09:46:30"}}} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"salesforce","crm_owner":143,"team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file...
|
[{"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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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.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":"108","depth":4,"bounds":{"left":0.72539896,"top":0.10055866,"width":0.011968086,"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 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:14] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:14] 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\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring start {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring end {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] 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\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:20] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55:20] 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\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55: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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] 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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] 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\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"12:50\",\"to\":\"12:55\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"02:45\",\"to\":\"02:50\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] 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\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":62.0} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-27T12:57:45.940375Z\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:05] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:05] 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\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring start {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring end {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] 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\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:11] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] 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\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"usage\":24777784,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"refreshToken\":\"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {\"team\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.28,\"average_seconds_per_request\":0.28} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {\"team\":2,\"strategies\":\"lastModified\",\"sync_count\":0,\"total\":0,\"last_synced_id\":null,\"duration_ms\":286.56} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"provider\":\"hubspot\",\"status\":\"completed\",\"duration_ms\":1417.45,\"usage\":25188000,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"usage\":25162720,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"33e34a7a-1c02-4f04-87ac-22c3a385e6e3\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":306,\"sociable_id\":109,\"provider_user_id\":\"11348452\",\"expires\":1701077403,\"refresh_token_expires\":null,\"provider\":\"hubspot\",\"state\":\"full-refresh\",\"auth_scope\":null,\"retry_after\":null,\"created_at\":\"2020-09-01 16:59:04\",\"updated_at\":\"2023-11-27 09:30:03\"}}} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":109,\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":28.83,\"usage\":25162928,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect.\"} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"usage\":25120440,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b\",\"account\":null} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":130,\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":50.47,\"usage\":25140104,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"usage\":25100792,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"71e3aac5-fb66-47c5-a236-2d051ae3e319\",\"account\":null} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":256,\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":61.91,\"usage\":25136552,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {\"host\":\"docker_lamp_1\",\"events\":2} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {\"userId\":\"641f1acb-16b8-42d1-8726-df52979dad0e\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":1500,\"sociable_id\":143,\"provider_user_id\":\"0052g000003frelAAA\",\"expires\":null,\"refresh_token_expires\":null,\"provider\":\"salesforce\",\"state\":\"full-refresh\",\"auth_scope\":\"refresh_token web api\",\"retry_after\":null,\"created_at\":\"2026-02-06 08:39:03\",\"updated_at\":\"2026-05-14 09:46:30\"}}} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"salesforce\",\"crm_owner\":143,\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}","depth":4,"bounds":{"left":0.4331782,"top":0.09736632,"width":0.5668218,"height":0.90263367},"on_screen":true,"value":"[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:14] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:14] 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\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring start {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring end {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] 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\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:20] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55:20] 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\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55: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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] 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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] 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\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"12:50\",\"to\":\"12:55\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"02:45\",\"to\":\"02:50\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] 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\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":62.0} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-27T12:57:45.940375Z\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:05] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:05] 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\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring start {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring end {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] 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\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:11] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] 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\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"usage\":24777784,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"refreshToken\":\"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {\"team\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.28,\"average_seconds_per_request\":0.28} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {\"team\":2,\"strategies\":\"lastModified\",\"sync_count\":0,\"total\":0,\"last_synced_id\":null,\"duration_ms\":286.56} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"provider\":\"hubspot\",\"status\":\"completed\",\"duration_ms\":1417.45,\"usage\":25188000,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"usage\":25162720,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"33e34a7a-1c02-4f04-87ac-22c3a385e6e3\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":306,\"sociable_id\":109,\"provider_user_id\":\"11348452\",\"expires\":1701077403,\"refresh_token_expires\":null,\"provider\":\"hubspot\",\"state\":\"full-refresh\",\"auth_scope\":null,\"retry_after\":null,\"created_at\":\"2020-09-01 16:59:04\",\"updated_at\":\"2023-11-27 09:30:03\"}}} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":109,\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":28.83,\"usage\":25162928,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect.\"} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"usage\":25120440,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b\",\"account\":null} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":130,\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":50.47,\"usage\":25140104,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"usage\":25100792,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"71e3aac5-fb66-47c5-a236-2d051ae3e319\",\"account\":null} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":256,\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":61.91,\"usage\":25136552,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {\"host\":\"docker_lamp_1\",\"events\":2} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {\"userId\":\"641f1acb-16b8-42d1-8726-df52979dad0e\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":1500,\"sociable_id\":143,\"provider_user_id\":\"0052g000003frelAAA\",\"expires\":null,\"refresh_token_expires\":null,\"provider\":\"salesforce\",\"state\":\"full-refresh\",\"auth_scope\":\"refresh_token web api\",\"retry_after\":null,\"created_at\":\"2026-02-06 08:39:03\",\"updated_at\":\"2026-05-14 09:46:30\"}}} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"salesforce\",\"crm_owner\":143,\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}","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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"}]...
|
-7116558536416563733
|
-2921165101966234307
|
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
16
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;
}
$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
108
Previous Highlighted Error
Next Highlighted Error
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:14] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:14] 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":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring start {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring end {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] 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":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:20] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55:20] 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":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55: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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] 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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {"correlation_id":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] 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":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:31] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesEnded {"from":"12:50","to":"12:55"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {"from":"02:45","to":"02:50"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] 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":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":59,"provider":"hubspot","refreshToken":"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":306,"provider":"hubspot","refreshToken":"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1372,"provider":"hubspot","refreshToken":"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {"total":3,"fixed":0,"failed":3} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {"memory_limit":"256M","max_execution_time":"0","initial_memory_mb":62.0} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {"expires_at":"2026-05-27T12:57:45.940375Z"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {"expires_in":1800,"cached_for":1500} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:05] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:05] 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":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring start {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring end {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] 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":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:11] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] 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":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","usage":24777784,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1499,"provider":"hubspot","refreshToken":"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {"socialAccountId":1499,"provider":"hubspot","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {"team":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {"team_id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/deals/search","total_requests":1,"total_records_fetched":0,"total_elapsed_seconds":0.28,"average_seconds_per_request":0.28} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {"team":2,"strategies":"lastModified","sync_count":0,"total":0,"last_synced_id":null,"duration_ms":286.56} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","provider":"hubspot","status":"completed","duration_ms":1417.45,"usage":25188000,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","usage":25162720,"real_usage":65011712,"pid":42210} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"33e34a7a-1c02-4f04-87ac-22c3a385e6e3","account":{"Jiminny\\Models\\SocialAccount":{"id":306,"sociable_id":109,"provider_user_id":"11348452","expires":1701077403,"refresh_token_expires":null,"provider":"hubspot","state":"full-refresh","auth_scope":null,"retry_after":null,"created_at":"2020-09-01 16:59:04","updated_at":"2023-11-27 09:30:03"}}} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":109,"team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","provider":"hubspot","status":"disconnected","duration_ms":28.83,"usage":25162928,"real_usage":65011712,"pid":42210,"reason":"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect."} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","usage":25120440,"real_usage":65011712,"pid":42210} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b","account":null} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":130,"team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","provider":"hubspot","status":"disconnected","duration_ms":50.47,"usage":25140104,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","usage":25100792,"real_usage":65011712,"pid":42210} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"71e3aac5-fb66-47c5-a236-2d051ae3e319","account":null} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":256,"team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","provider":"hubspot","status":"disconnected","duration_ms":61.91,"usage":25136552,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {"host":"docker_lamp_1"} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {"host":"docker_lamp_1","events":2} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {"userId":"641f1acb-16b8-42d1-8726-df52979dad0e","account":{"Jiminny\\Models\\SocialAccount":{"id":1500,"sociable_id":143,"provider_user_id":"0052g000003frelAAA","expires":null,"refresh_token_expires":null,"provider":"salesforce","state":"full-refresh","auth_scope":"refresh_token web api","retry_after":null,"created_at":"2026-02-06 08:39:03","updated_at":"2026-05-14 09:46:30"}}} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"salesforce","crm_owner":143,"team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file...
|
78723
|
NULL
|
NULL
|
NULL
|
|
78725
|
2763
|
27
|
2026-05-27T12:56:36.791047+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886596791_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
16
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;
}
$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
108
Previous Highlighted Error
Next Highlighted Error...
|
[{"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":"16","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\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\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},{"role":"AXStaticText","text":"108","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}]...
|
5035782338932105749
|
-6434304657575212149
|
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
16
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;
}
$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
108
Previous Highlighted Error
Next Highlighted Error...
|
78724
|
NULL
|
NULL
|
NULL
|
|
78724
|
2763
|
26
|
2026-05-27T12:56:35.830015+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886595830_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
16
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;
}
$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
6
Previous Highlighted Error
Next Highlighted Error
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder...
|
[{"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":"16","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\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\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":"6","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":"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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qai.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_staging.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_subenv.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Acl, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activity, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dtos, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AWS, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Cache, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Country, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Database, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Datadog, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DateTime, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection, folder","depth":9,"on_screen":false,"role_description":"text"}]...
|
-4538651910930564120
|
-1250714193234720373
|
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
16
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;
}
$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
6
Previous Highlighted Error
Next Highlighted Error
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78723
|
2764
|
28
|
2026-05-27T12:56:35.631818+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886595631_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
16
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;
}
$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
6
Previous Highlighted Error
Next Highlighted Error
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf...
|
[{"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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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":"6","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":"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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qai.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_staging.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_subenv.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"on_screen":false,"role_description":"text"}]...
|
-755122716377735253
|
-1827192610738501205
|
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
16
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;
}
$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
6
Previous Highlighted Error
Next Highlighted Error
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78722
|
2763
|
25
|
2026-05-27T12:56:34.724714+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886594724_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
16
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;
}
$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
6
Previous Highlighted Error
Next Highlighted Error
"dealstage": {
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"source": "CRM_UI",
"sourceId": "userId:71523846",
"updatedByUserId": 71523846,
"versions": [
{
"name": "dealstage",
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"sourceId": "userId:71523846",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019e06c6-cd38-76f4-ae81-2d85ca04a72c",
"updatedByUserId": 71523846,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "InboundDbObjects-Service-web"
},
{
"name": "dealstage",
"value": "735341516",
"timestamp": 1773305812311, # 2026-03-12 08:56:52
"sourceId": "userId:76091797",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019ce143-5502-7857-86c1-e4699e3d745d",
"updatedByUserId": 76091797,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "CrmObjectBuilderRpcServer-userweb"
}
]
},
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions...
|
[{"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":"16","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\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\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":"6","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":"\"dealstage\": {\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"source\": \"CRM_UI\",\n \"sourceId\": \"userId:71523846\",\n \"updatedByUserId\": 71523846,\n \"versions\": [\n {\n \"name\": \"dealstage\",\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"sourceId\": \"userId:71523846\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019e06c6-cd38-76f4-ae81-2d85ca04a72c\",\n \"updatedByUserId\": 71523846,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"InboundDbObjects-Service-web\"\n },\n {\n \"name\": \"dealstage\",\n \"value\": \"735341516\",\n \"timestamp\": 1773305812311, # 2026-03-12 08:56:52\n \"sourceId\": \"userId:76091797\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019ce143-5502-7857-86c1-e4699e3d745d\",\n \"updatedByUserId\": 76091797,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"CrmObjectBuilderRpcServer-userweb\"\n }\n ]\n},","depth":4,"on_screen":true,"value":"\"dealstage\": {\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"source\": \"CRM_UI\",\n \"sourceId\": \"userId:71523846\",\n \"updatedByUserId\": 71523846,\n \"versions\": [\n {\n \"name\": \"dealstage\",\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"sourceId\": \"userId:71523846\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019e06c6-cd38-76f4-ae81-2d85ca04a72c\",\n \"updatedByUserId\": 71523846,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"InboundDbObjects-Service-web\"\n },\n {\n \"name\": \"dealstage\",\n \"value\": \"735341516\",\n \"timestamp\": 1773305812311, # 2026-03-12 08:56:52\n \"sourceId\": \"userId:76091797\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019ce143-5502-7857-86c1-e4699e3d745d\",\n \"updatedByUserId\": 76091797,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"CrmObjectBuilderRpcServer-userweb\"\n }\n ]\n},","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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qai.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_staging.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_subenv.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"on_screen":false,"role_description":"text"}]...
|
-1850671007154827527
|
-1971307730094880373
|
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
16
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;
}
$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
6
Previous Highlighted Error
Next Highlighted Error
"dealstage": {
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"source": "CRM_UI",
"sourceId": "userId:71523846",
"updatedByUserId": 71523846,
"versions": [
{
"name": "dealstage",
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"sourceId": "userId:71523846",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019e06c6-cd38-76f4-ae81-2d85ca04a72c",
"updatedByUserId": 71523846,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "InboundDbObjects-Service-web"
},
{
"name": "dealstage",
"value": "735341516",
"timestamp": 1773305812311, # 2026-03-12 08:56:52
"sourceId": "userId:76091797",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019ce143-5502-7857-86c1-e4699e3d745d",
"updatedByUserId": 76091797,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "CrmObjectBuilderRpcServer-userweb"
}
]
},
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions...
|
78720
|
NULL
|
NULL
|
NULL
|
|
78721
|
2764
|
27
|
2026-05-27T12:56:34.624150+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886594624_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.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
16
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;
}
$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
6
Previous Highlighted Error...
|
[{"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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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":"6","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"on_screen":false,"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}]...
|
5361353910361124162
|
-6438807707444672117
|
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
16
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;
}
$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
6
Previous Highlighted Error...
|
78719
|
NULL
|
NULL
|
NULL
|
|
78720
|
2763
|
24
|
2026-05-27T12:56:33.342345+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886593342_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.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
16
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;
}
$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
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:14] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:14] 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":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring start {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring end {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] 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":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:20] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55:20] 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":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55: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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] 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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {"correlation_id":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] 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":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:31] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesEnded {"from":"12:50","to":"12:55"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {"from":"02:45","to":"02:50"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] 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":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":59,"provider":"hubspot","refreshToken":"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":306,"provider":"hubspot","refreshToken":"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1372,"provider":"hubspot","refreshToken":"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {"total":3,"fixed":0,"failed":3} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {"memory_limit":"256M","max_execution_time":"0","initial_memory_mb":62.0} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {"expires_at":"2026-05-27T12:57:45.940375Z"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {"expires_in":1800,"cached_for":1500} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:05] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:05] 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":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring start {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring end {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] 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":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:11] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] 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":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","usage":24777784,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1499,"provider":"hubspot","refreshToken":"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {"socialAccountId":1499,"provider":"hubspot","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {"team":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {"team_id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/deals/search","total_requests":1,"total_records_fetched":0,"total_elapsed_seconds":0.28,"average_seconds_per_request":0.28} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {"team":2,"strategies":"lastModified","sync_count":0,"total":0,"last_synced_id":null,"duration_ms":286.56} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","provider":"hubspot","status":"completed","duration_ms":1417.45,"usage":25188000,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","usage":25162720,"real_usage":65011712,"pid":42210} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"33e34a7a-1c02-4f04-87ac-22c3a385e6e3","account":{"Jiminny\\Models\\SocialAccount":{"id":306,"sociable_id":109,"provider_user_id":"11348452","expires":1701077403,"refresh_token_expires":null,"provider":"hubspot","state":"full-refresh","auth_scope":null,"retry_after":null,"created_at":"2020-09-01 16:59:04","updated_at":"2023-11-27 09:30:03"}}} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":109,"team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","provider":"hubspot","status":"disconnected","duration_ms":28.83,"usage":25162928,"real_usage":65011712,"pid":42210,"reason":"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect."} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","usage":25120440,"real_usage":65011712,"pid":42210} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b","account":null} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":130,"team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","provider":"hubspot","status":"disconnected","duration_ms":50.47,"usage":25140104,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","usage":25100792,"real_usage":65011712,"pid":42210} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"71e3aac5-fb66-47c5-a236-2d051ae3e319","account":null} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":256,"team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","provider":"hubspot","status":"disconnected","duration_ms":61.91,"usage":25136552,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {"host":"docker_lamp_1"} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {"host":"docker_lamp_1","events":2} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {"userId":"641f1acb-16b8-42d1-8726-df52979dad0e","account":{"Jiminny\\Models\\SocialAccount":{"id":1500,"sociable_id":143,"provider_user_id":"0052g000003frelAAA","expires":null,"refresh_token_expires":null,"provider":"salesforce","state":"full-refresh","auth_scope":"refresh_token web api","retry_after":null,"created_at":"2026-02-06 08:39:03","updated_at":"2026-05-14 09:46:30"}}} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"salesforce","crm_owner":143,"team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder...
|
[{"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":"16","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\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\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},{"role":"AXTextArea","text":"[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:14] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:14] 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\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring start {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring end {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] 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\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:20] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55:20] 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\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55: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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] 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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] 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\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"12:50\",\"to\":\"12:55\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"02:45\",\"to\":\"02:50\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] 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\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":62.0} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-27T12:57:45.940375Z\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:05] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:05] 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\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring start {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring end {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] 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\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:11] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] 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\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"usage\":24777784,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"refreshToken\":\"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {\"team\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.28,\"average_seconds_per_request\":0.28} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {\"team\":2,\"strategies\":\"lastModified\",\"sync_count\":0,\"total\":0,\"last_synced_id\":null,\"duration_ms\":286.56} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"provider\":\"hubspot\",\"status\":\"completed\",\"duration_ms\":1417.45,\"usage\":25188000,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"usage\":25162720,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"33e34a7a-1c02-4f04-87ac-22c3a385e6e3\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":306,\"sociable_id\":109,\"provider_user_id\":\"11348452\",\"expires\":1701077403,\"refresh_token_expires\":null,\"provider\":\"hubspot\",\"state\":\"full-refresh\",\"auth_scope\":null,\"retry_after\":null,\"created_at\":\"2020-09-01 16:59:04\",\"updated_at\":\"2023-11-27 09:30:03\"}}} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":109,\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":28.83,\"usage\":25162928,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect.\"} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"usage\":25120440,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b\",\"account\":null} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":130,\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":50.47,\"usage\":25140104,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"usage\":25100792,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"71e3aac5-fb66-47c5-a236-2d051ae3e319\",\"account\":null} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":256,\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":61.91,\"usage\":25136552,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {\"host\":\"docker_lamp_1\",\"events\":2} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {\"userId\":\"641f1acb-16b8-42d1-8726-df52979dad0e\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":1500,\"sociable_id\":143,\"provider_user_id\":\"0052g000003frelAAA\",\"expires\":null,\"refresh_token_expires\":null,\"provider\":\"salesforce\",\"state\":\"full-refresh\",\"auth_scope\":\"refresh_token web api\",\"retry_after\":null,\"created_at\":\"2026-02-06 08:39:03\",\"updated_at\":\"2026-05-14 09:46:30\"}}} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"salesforce\",\"crm_owner\":143,\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}","depth":4,"on_screen":true,"value":"[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:14] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:14] 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\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring start {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring end {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] 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\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:20] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55:20] 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\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55: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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] 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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] 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\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"12:50\",\"to\":\"12:55\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"02:45\",\"to\":\"02:50\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] 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\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":62.0} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-27T12:57:45.940375Z\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:05] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:05] 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\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring start {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring end {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] 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\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:11] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] 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\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"usage\":24777784,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"refreshToken\":\"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {\"team\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.28,\"average_seconds_per_request\":0.28} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {\"team\":2,\"strategies\":\"lastModified\",\"sync_count\":0,\"total\":0,\"last_synced_id\":null,\"duration_ms\":286.56} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"provider\":\"hubspot\",\"status\":\"completed\",\"duration_ms\":1417.45,\"usage\":25188000,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"usage\":25162720,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"33e34a7a-1c02-4f04-87ac-22c3a385e6e3\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":306,\"sociable_id\":109,\"provider_user_id\":\"11348452\",\"expires\":1701077403,\"refresh_token_expires\":null,\"provider\":\"hubspot\",\"state\":\"full-refresh\",\"auth_scope\":null,\"retry_after\":null,\"created_at\":\"2020-09-01 16:59:04\",\"updated_at\":\"2023-11-27 09:30:03\"}}} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":109,\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":28.83,\"usage\":25162928,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect.\"} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"usage\":25120440,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b\",\"account\":null} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":130,\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":50.47,\"usage\":25140104,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"usage\":25100792,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"71e3aac5-fb66-47c5-a236-2d051ae3e319\",\"account\":null} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":256,\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":61.91,\"usage\":25136552,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {\"host\":\"docker_lamp_1\",\"events\":2} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {\"userId\":\"641f1acb-16b8-42d1-8726-df52979dad0e\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":1500,\"sociable_id\":143,\"provider_user_id\":\"0052g000003frelAAA\",\"expires\":null,\"refresh_token_expires\":null,\"provider\":\"salesforce\",\"state\":\"full-refresh\",\"auth_scope\":\"refresh_token web api\",\"retry_after\":null,\"created_at\":\"2026-02-06 08:39:03\",\"updated_at\":\"2026-05-14 09:46:30\"}}} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"salesforce\",\"crm_owner\":143,\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}","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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qai.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_staging.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_subenv.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Acl, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activity, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dtos, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AWS, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Cache, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Country, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Database, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Datadog, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DateTime, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"on_screen":false,"role_description":"text"}]...
|
-4445563490604123824
|
-2921165651722048195
|
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
16
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;
}
$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
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:14] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:14] 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":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring start {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring end {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] 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":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:20] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55:20] 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":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55: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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] 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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {"correlation_id":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] 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":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:31] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesEnded {"from":"12:50","to":"12:55"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {"from":"02:45","to":"02:50"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] 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":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":59,"provider":"hubspot","refreshToken":"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":306,"provider":"hubspot","refreshToken":"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1372,"provider":"hubspot","refreshToken":"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {"total":3,"fixed":0,"failed":3} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {"memory_limit":"256M","max_execution_time":"0","initial_memory_mb":62.0} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {"expires_at":"2026-05-27T12:57:45.940375Z"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {"expires_in":1800,"cached_for":1500} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:05] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:05] 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":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring start {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring end {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] 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":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:11] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] 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":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","usage":24777784,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1499,"provider":"hubspot","refreshToken":"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {"socialAccountId":1499,"provider":"hubspot","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {"team":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {"team_id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/deals/search","total_requests":1,"total_records_fetched":0,"total_elapsed_seconds":0.28,"average_seconds_per_request":0.28} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {"team":2,"strategies":"lastModified","sync_count":0,"total":0,"last_synced_id":null,"duration_ms":286.56} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","provider":"hubspot","status":"completed","duration_ms":1417.45,"usage":25188000,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","usage":25162720,"real_usage":65011712,"pid":42210} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"33e34a7a-1c02-4f04-87ac-22c3a385e6e3","account":{"Jiminny\\Models\\SocialAccount":{"id":306,"sociable_id":109,"provider_user_id":"11348452","expires":1701077403,"refresh_token_expires":null,"provider":"hubspot","state":"full-refresh","auth_scope":null,"retry_after":null,"created_at":"2020-09-01 16:59:04","updated_at":"2023-11-27 09:30:03"}}} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":109,"team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","provider":"hubspot","status":"disconnected","duration_ms":28.83,"usage":25162928,"real_usage":65011712,"pid":42210,"reason":"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect."} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","usage":25120440,"real_usage":65011712,"pid":42210} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b","account":null} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":130,"team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","provider":"hubspot","status":"disconnected","duration_ms":50.47,"usage":25140104,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","usage":25100792,"real_usage":65011712,"pid":42210} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"71e3aac5-fb66-47c5-a236-2d051ae3e319","account":null} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":256,"team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","provider":"hubspot","status":"disconnected","duration_ms":61.91,"usage":25136552,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {"host":"docker_lamp_1"} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {"host":"docker_lamp_1","events":2} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {"userId":"641f1acb-16b8-42d1-8726-df52979dad0e","account":{"Jiminny\\Models\\SocialAccount":{"id":1500,"sociable_id":143,"provider_user_id":"0052g000003frelAAA","expires":null,"refresh_token_expires":null,"provider":"salesforce","state":"full-refresh","auth_scope":"refresh_token web api","retry_after":null,"created_at":"2026-02-06 08:39:03","updated_at":"2026-05-14 09:46:30"}}} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"salesforce","crm_owner":143,"team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78719
|
2764
|
26
|
2026-05-27T12:56:33.239437+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886593239_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
16
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;
}
$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
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:14] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:14] 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":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring start {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring end {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] 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":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:20] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55:20] 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":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55: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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] 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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {"correlation_id":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] 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":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:31] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesEnded {"from":"12:50","to":"12:55"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {"from":"02:45","to":"02:50"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] 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":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":59,"provider":"hubspot","refreshToken":"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":306,"provider":"hubspot","refreshToken":"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1372,"provider":"hubspot","refreshToken":"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {"total":3,"fixed":0,"failed":3} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {"memory_limit":"256M","max_execution_time":"0","initial_memory_mb":62.0} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {"expires_at":"2026-05-27T12:57:45.940375Z"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {"expires_in":1800,"cached_for":1500} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:05] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:05] 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":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring start {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring end {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] 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":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:11] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] 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":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","usage":24777784,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1499,"provider":"hubspot","refreshToken":"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {"socialAccountId":1499,"provider":"hubspot","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {"team":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {"team_id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/deals/search","total_requests":1,"total_records_fetched":0,"total_elapsed_seconds":0.28,"average_seconds_per_request":0.28} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {"team":2,"strategies":"lastModified","sync_count":0,"total":0,"last_synced_id":null,"duration_ms":286.56} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","provider":"hubspot","status":"completed","duration_ms":1417.45,"usage":25188000,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","usage":25162720,"real_usage":65011712,"pid":42210} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"33e34a7a-1c02-4f04-87ac-22c3a385e6e3","account":{"Jiminny\\Models\\SocialAccount":{"id":306,"sociable_id":109,"provider_user_id":"11348452","expires":1701077403,"refresh_token_expires":null,"provider":"hubspot","state":"full-refresh","auth_scope":null,"retry_after":null,"created_at":"2020-09-01 16:59:04","updated_at":"2023-11-27 09:30:03"}}} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":109,"team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","provider":"hubspot","status":"disconnected","duration_ms":28.83,"usage":25162928,"real_usage":65011712,"pid":42210,"reason":"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect."} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","usage":25120440,"real_usage":65011712,"pid":42210} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b","account":null} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":130,"team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","provider":"hubspot","status":"disconnected","duration_ms":50.47,"usage":25140104,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","usage":25100792,"real_usage":65011712,"pid":42210} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"71e3aac5-fb66-47c5-a236-2d051ae3e319","account":null} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":256,"team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","provider":"hubspot","status":"disconnected","duration_ms":61.91,"usage":25136552,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {"host":"docker_lamp_1"} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {"host":"docker_lamp_1","events":2} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {"userId":"641f1acb-16b8-42d1-8726-df52979dad0e","account":{"Jiminny\\Models\\SocialAccount":{"id":1500,"sociable_id":143,"provider_user_id":"0052g000003frelAAA","expires":null,"refresh_token_expires":null,"provider":"salesforce","state":"full-refresh","auth_scope":"refresh_token web api","retry_after":null,"created_at":"2026-02-06 08:39:03","updated_at":"2026-05-14 09:46:30"}}} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"salesforce","crm_owner":143,"team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}...
|
[{"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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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.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":"AXTextArea","text":"[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:14] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:14] 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\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring start {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring end {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] 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\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:20] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55:20] 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\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55: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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] 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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] 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\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"12:50\",\"to\":\"12:55\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"02:45\",\"to\":\"02:50\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] 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\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":62.0} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-27T12:57:45.940375Z\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:05] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:05] 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\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring start {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring end {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] 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\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:11] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] 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\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"usage\":24777784,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"refreshToken\":\"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {\"team\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.28,\"average_seconds_per_request\":0.28} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {\"team\":2,\"strategies\":\"lastModified\",\"sync_count\":0,\"total\":0,\"last_synced_id\":null,\"duration_ms\":286.56} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"provider\":\"hubspot\",\"status\":\"completed\",\"duration_ms\":1417.45,\"usage\":25188000,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"usage\":25162720,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"33e34a7a-1c02-4f04-87ac-22c3a385e6e3\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":306,\"sociable_id\":109,\"provider_user_id\":\"11348452\",\"expires\":1701077403,\"refresh_token_expires\":null,\"provider\":\"hubspot\",\"state\":\"full-refresh\",\"auth_scope\":null,\"retry_after\":null,\"created_at\":\"2020-09-01 16:59:04\",\"updated_at\":\"2023-11-27 09:30:03\"}}} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":109,\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":28.83,\"usage\":25162928,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect.\"} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"usage\":25120440,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b\",\"account\":null} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":130,\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":50.47,\"usage\":25140104,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"usage\":25100792,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"71e3aac5-fb66-47c5-a236-2d051ae3e319\",\"account\":null} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":256,\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":61.91,\"usage\":25136552,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {\"host\":\"docker_lamp_1\",\"events\":2} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {\"userId\":\"641f1acb-16b8-42d1-8726-df52979dad0e\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":1500,\"sociable_id\":143,\"provider_user_id\":\"0052g000003frelAAA\",\"expires\":null,\"refresh_token_expires\":null,\"provider\":\"salesforce\",\"state\":\"full-refresh\",\"auth_scope\":\"refresh_token web api\",\"retry_after\":null,\"created_at\":\"2026-02-06 08:39:03\",\"updated_at\":\"2026-05-14 09:46:30\"}}} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"salesforce\",\"crm_owner\":143,\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}","depth":4,"bounds":{"left":0.4331782,"top":0.09736632,"width":0.5668218,"height":0.90263367},"on_screen":true,"value":"[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:12] 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\":\"41d4bbce-5baf-49aa-a276-c1c74a65020a\",\"trace_id\":\"1d0d4404-bf0a-4053-84ca-68be39511779\"}\n[2026-05-27 12:55:14] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:14] 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\":\"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d\",\"trace_id\":\"aedb441d-c947-4f26-a1cd-056e48b6f5ed\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring start {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:15] local.NOTICE: Monitoring end {\"correlation_id\":\"56b0df60-e16c-4c60-a577-f394e33e2ddc\",\"trace_id\":\"ba2c277f-5516-49b6-854a-b4f446a1cb6c\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:17] 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\":\"3b3db768-2cbe-4893-a39d-39c1ed923f29\",\"trace_id\":\"dedb9613-95f2-47d1-a51b-82fff1514a22\"}\n[2026-05-27 12:55:18] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:18] 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\":\"75fb79e4-71d7-4533-94e6-ed2ca6531e11\",\"trace_id\":\"4d9236e2-51d7-475a-99d2-487589468575\"}\n[2026-05-27 12:55:20] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55:20] 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\":\"5e1f4a28-d081-4c59-8d76-a116dadc701e\",\"trace_id\":\"b7e8d289-707b-47c1-9d6c-f47ebad1e01b\"}\n[2026-05-27 12:55: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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"expected_alias\":\"catch-all\",\"expected_host\":\"txt.staging.jiminny.com\"} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {\"mailbox\":\"catch-all@txt.staging.jiminny.com\",\"messages_processed\":0,\"message_ids\":[]} {\"correlation_id\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55:22] 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\":\"a6659ab1-f9e9-458f-8b49-b65216d5141f\",\"trace_id\":\"2f832b37-0925-4801-a2e0-80f234c834ce\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55: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\":\"87e0c627-3642-4d17-bf25-264f6fdc1969\",\"trace_id\":\"3b555fb3-83d8-4284-bb7c-0f783b4ed083\"}\n[2026-05-27 12:55:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {\"correlation_id\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:28] 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\":\"79376862-86ae-416a-9af3-2f790e35c610\",\"trace_id\":\"85b2da1c-9140-4d3d-ab55-9577deac7825\"}\n[2026-05-27 12:55:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"12:50\",\"to\":\"12:55\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"02:45\",\"to\":\"02:50\"} {\"correlation_id\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:31] 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\":\"e922989f-9f26-4870-be4e-a3ed274edd06\",\"trace_id\":\"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2\"}\n[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] 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\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"35ad5085-589b-4afa-8ab6-194229ce310f\",\"trace_id\":\"fcb90117-62fd-4a79-967a-c93b797a9aab\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"6e057ca5-65bf-41f6-b8af-08e715bc654d\",\"trace_id\":\"6aea00b5-6221-4847-8961-7a86ef3210cd\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":62.0} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-27T12:57:45.940375Z\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"85c09ea4-f8d1-4cc2-b767-a5f2d957d016\",\"trace_id\":\"4fe5a8fa-1ac5-407c-926c-83f504d762d4\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {\"count\":0} {\"correlation_id\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:03] 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\":\"52d58fe6-511d-4783-83d2-2cd095621565\",\"trace_id\":\"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12\"}\n[2026-05-27 12:56:05] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"dialers:monitor-activities\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:05] 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\":\"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1\",\"trace_id\":\"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring start {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:06] local.NOTICE: Monitoring end {\"correlation_id\":\"dc41678d-bf39-4a6e-870f-d38c97eff1c3\",\"trace_id\":\"10976781-275a-4aee-a84d-b6fa96ad2602\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:08] 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\":\"45813b78-011b-4ba5-8928-5a82bdd9f4ab\",\"trace_id\":\"f88e2e25-2c4f-4690-a49b-ccc8fb856866\"}\n[2026-05-27 12:56:09] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:09] 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\":\"91da18c1-d417-4b39-a4a0-c7cb5e976f70\",\"trace_id\":\"11d77bc4-1411-4cc3-a44d-0acca49e3d40\"}\n[2026-05-27 12:56:11] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:count\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {\"correlation_id\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:11] 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\":\"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c\",\"trace_id\":\"ee07bdc6-4918-4540-8682-07bc204cf6e5\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {\"offset\":\"\",\"jiminny_team_id\":1} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {\"url\":\"https://api.hubapi.com/webhooks/v4/journal/latest\"} {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"f7da3683-aefb-4f36-9d2d-9b731e7e44df\",\"trace_id\":\"71499f0c-b12f-4a08-af95-4f390940c5c2\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:sync-hubspot-objects\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"aab715c2-fbe0-4a65-9292-b2b218775424\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"usage\":24777784,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"refreshToken\":\"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {\"socialAccountId\":1499,\"provider\":\"hubspot\",\"state\":\"connected\"} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {\"team\":2} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.28,\"average_seconds_per_request\":0.28} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {\"team\":2,\"strategies\":\"lastModified\",\"sync_count\":0,\"total\":0,\"last_synced_id\":null,\"duration_ms\":286.56} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4\",\"provider\":\"hubspot\",\"status\":\"completed\",\"duration_ms\":1417.45,\"usage\":25188000,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"84144bb1-43a6-40fd-9a9c-0b259220caa0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"usage\":25162720,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"33e34a7a-1c02-4f04-87ac-22c3a385e6e3\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":306,\"sociable_id\":109,\"provider_user_id\":\"11348452\",\"expires\":1701077403,\"refresh_token_expires\":null,\"provider\":\"hubspot\",\"state\":\"full-refresh\",\"auth_scope\":null,\"retry_after\":null,\"created_at\":\"2020-09-01 16:59:04\",\"updated_at\":\"2023-11-27 09:30:03\"}}} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":109,\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":29} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2b115eb-93ce-4d1b-929c-173757df8fba\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":28.83,\"usage\":25162928,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect.\"} {\"correlation_id\":\"d16fc90d-2daf-4985-a507-10c03db9daf0\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"usage\":25120440,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b\",\"account\":null} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":130,\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":42} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"b2d49a54-b645-4637-a7ae-a86cfce6e8e4\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":50.47,\"usage\":25140104,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"69d51f2f-966d-4ce3-a3b0-13db04d24441\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"usage\":25100792,\"real_usage\":65011712,\"pid\":42210} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {\"userId\":\"71e3aac5-fb66-47c5-a236-2d051ae3e319\",\"account\":null} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"hubspot\",\"crm_owner\":256,\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"hubspot\",\"team_id\":49} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {\"team\":\"c6b9d6b0-b48d-4832-a68c-a57d60651888\",\"provider\":\"hubspot\",\"status\":\"disconnected\",\"duration_ms\":61.91,\"usage\":25136552,\"real_usage\":65011712,\"pid\":42210,\"reason\":\"Social account for HubSpot cannot be found. Please login to Jiminny to connect.\"} {\"correlation_id\":\"27759f57-4cfe-4fd0-a357-ccb54433120b\",\"trace_id\":\"15667b2a-219a-4ecf-8397-f8b5729ba263\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:15] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:notify-not-logged\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"8adbd852-a071-43d7-9393-80d2ceb0461e\",\"trace_id\":\"f2fefab6-f987-4a8d-8ba9-98e358be3479\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {\"host\":\"docker_lamp_1\",\"events\":2} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:19] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":62.0,\"memoryPeakBeforeCommandInMb\":99.883,\"memoryPeakAfterCommandInMB\":99.883} {\"correlation_id\":\"1ef33eb4-1aeb-441d-bee8-259627a93960\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":59} {\"correlation_id\":\"cf9cc0f9-72df-468a-9dfd-7419e132cd3c\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {\"userId\":\"641f1acb-16b8-42d1-8726-df52979dad0e\",\"account\":{\"Jiminny\\\\Models\\\\SocialAccount\":{\"id\":1500,\"sociable_id\":143,\"provider_user_id\":\"0052g000003frelAAA\",\"expires\":null,\"refresh_token_expires\":null,\"provider\":\"salesforce\",\"state\":\"full-refresh\",\"auth_scope\":\"refresh_token web api\",\"retry_after\":null,\"created_at\":\"2026-02-06 08:39:03\",\"updated_at\":\"2026-05-14 09:46:30\"}}} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {\"crm_provider\":\"salesforce\",\"crm_owner\":143,\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {\"crm_provider\":\"salesforce\",\"team_id\":1} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}\n[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {\"inbox_id\":212} {\"correlation_id\":\"2ebe7131-99ad-40ee-88ce-054a8755ea10\",\"trace_id\":\"566f32c7-b8d7-4e45-800e-124b5da5319e\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false}]...
|
-1713126740092526146
|
-2921165101966234307
|
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
16
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;
}
$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
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:12] 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":"41d4bbce-5baf-49aa-a276-c1c74a65020a","trace_id":"1d0d4404-bf0a-4053-84ca-68be39511779"}
[2026-05-27 12:55:14] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:14] 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":"77fbb54b-1a7b-48ca-82e1-2ad6d2b51b4d","trace_id":"aedb441d-c947-4f26-a1cd-056e48b6f5ed"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring start {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:15] local.NOTICE: Monitoring end {"correlation_id":"56b0df60-e16c-4c60-a577-f394e33e2ddc","trace_id":"ba2c277f-5516-49b6-854a-b4f446a1cb6c"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:17] 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":"3b3db768-2cbe-4893-a39d-39c1ed923f29","trace_id":"dedb9613-95f2-47d1-a51b-82fff1514a22"}
[2026-05-27 12:55:18] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:18] 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":"75fb79e4-71d7-4533-94e6-ed2ca6531e11","trace_id":"4d9236e2-51d7-475a-99d2-487589468575"}
[2026-05-27 12:55:20] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:purge-stale","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55:20] 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":"5e1f4a28-d081-4c59-8d76-a116dadc701e","trace_id":"b7e8d289-707b-47c1-9d6c-f47ebad1e01b"}
[2026-05-27 12:55: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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:21] local.INFO: [TextRelayService] Starting sync {"mailbox":"[EMAIL]","expected_alias":"catch-all","expected_host":"txt.staging.jiminny.com"} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] local.INFO: [TextRelayService] Sync completed {"mailbox":"[EMAIL]","messages_processed":0,"message_ids":[]} {"correlation_id":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55:22] 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":"a6659ab1-f9e9-458f-8b49-b65216d5141f","trace_id":"2f832b37-0925-4801-a2e0-80f234c834ce"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:25] local.INFO: Running pre-meeting notification command {"correlation_id":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55: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":"87e0c627-3642-4d17-bf25-264f6fdc1969","trace_id":"3b555fb3-83d8-4284-bb7c-0f783b4ed083"}
[2026-05-27 12:55:28] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:start","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: Running conference:monitor:start command for activities in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] local.INFO: [conference:monitor:start] No activities found in (2026-05-27 12:45:00, 2026-05-27 12:50:00] {"correlation_id":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:28] 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":"79376862-86ae-416a-9af3-2f790e35c610","trace_id":"85b2da1c-9140-4d3d-ab55-9577deac7825"}
[2026-05-27 12:55:31] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:end","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesEnded {"from":"12:50","to":"12:55"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] local.INFO: conference:monitor:end:Jiminny\Console\Commands\Activities\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {"from":"02:45","to":"02:50"} {"correlation_id":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:31] 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":"e922989f-9f26-4870-be4e-a3ed274edd06","trace_id":"6f0dd2a0-4562-40df-a1da-a4c6fdc0abf2"}
[2026-05-27 12:55:38] local.NOTICE: Repairing HubSpot tokens start {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: Trying to refresh HubSpot token {"account_id":59,"updated_at":"2025-10-03 09:32:05"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:38] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":59,"provider":"hubspot","refreshToken":"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: Trying to refresh HubSpot token {"account_id":306,"updated_at":"2023-11-27 09:30:03"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:39] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":306,"provider":"hubspot","refreshToken":"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: Trying to refresh HubSpot token {"account_id":1372,"updated_at":"2025-10-02 14:47:06"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1372,"provider":"hubspot","refreshToken":"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4","state":"full-refresh"} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] 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":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:40] local.NOTICE: Repairing HubSpot tokens end {"total":3,"fixed":0,"failed":3} {"correlation_id":"35ad5085-589b-4afa-8ab6-194229ce310f","trace_id":"fcb90117-62fd-4a79-967a-c93b797a9aab"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:bullhorn:ping","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"6e057ca5-65bf-41f6-b8af-08e715bc654d","trace_id":"6aea00b5-6221-4847-8961-7a86ef3210cd"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Command] Starting polling service {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Service starting {"memory_limit":"256M","max_execution_time":"0","initial_memory_mb":62.0} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Acquired polling lock {"expires_at":"2026-05-27T12:57:45.940375Z"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"conference:pre-meeting-reminder","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"85c09ea4-f8d1-4cc2-b767-a5f2d957d016","trace_id":"4fe5a8fa-1ac5-407c-926c-83f504d762d4"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:45] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {"expires_in":1800,"cached_for":1500} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:46] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:51] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:55:56] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] local.INFO: [ScheduleBotCommand] Dispatched activities to capture {"count":0} {"correlation_id":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:03] 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":"52d58fe6-511d-4783-83d2-2cd095621565","trace_id":"3bae8a57-cba4-40c0-bc23-bc6e5df4ba12"}
[2026-05-27 12:56:05] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"dialers:monitor-activities","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:05] 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":"b561c2f8-d5e0-4b0c-89d4-3e0092a3ddc1","trace_id":"d6f35eac-1ec1-441d-87f2-2e38a4d4e4a4"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring start {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:06] local.NOTICE: Monitoring end {"correlation_id":"dc41678d-bf39-4a6e-870f-d38c97eff1c3","trace_id":"10976781-275a-4aee-a84d-b6fa96ad2602"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:08] 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":"45813b78-011b-4ba5-8928-5a82bdd9f4ab","trace_id":"f88e2e25-2c4f-4690-a49b-ccc8fb856866"}
[2026-05-27 12:56:09] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:batch:process","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] STARTING batch process {"host":"docker_lamp_1"} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] local.INFO: [EmailSchedule] FINISHED batch process {"host":"docker_lamp_1","processed":0} {"correlation_id":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:09] 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":"91da18c1-d417-4b39-a4a0-c7cb5e976f70","trace_id":"11d77bc4-1411-4cc3-a44d-0acca49e3d40"}
[2026-05-27 12:56:11] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"conference:monitor:count","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: Running conference:monitor:count command for activities in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] local.INFO: [conference:monitor:count] No activities found in (2026-05-27 12:54:00, 2026-05-27 12:56:00] {"correlation_id":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:11] 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":"3a28ecc0-c67a-4f9d-bf36-2cfcb024ef6c","trace_id":"ee07bdc6-4918-4540-8682-07bc204cf6e5"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] Getting offset from database {"offset":"","jiminny_team_id":1} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal API] Fetching latest journal entry {"url":"https://api.hubapi.com/webhooks/v4/journal/latest"} {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:12] local.INFO: [HubSpot Journal Polling] No data {"correlation_id":"f7da3683-aefb-4f36-9d2d-9b731e7e44df","trace_id":"71499f0c-b12f-4a08-af95-4f390940c5c2"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"crm:sync-hubspot-objects","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"aab715c2-fbe0-4a65-9292-b2b218775424","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SyncHubspotObjects] Starting sync {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","usage":24777784,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Token needs refreshing {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:13] local.INFO: [SocialAccountService] Refreshing token from provider {"socialAccountId":1499,"provider":"hubspot","refreshToken":"96f94c623a404e02ebdbf07f1b75707bb6cdbf848cbf45d418baf608c41a8d86","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Saving model {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountObserver] Access token was modified, encrypting {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [SocialAccountService] Token refreshed {"socialAccountId":1499,"provider":"hubspot","state":"connected"} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:14] local.INFO: [HubSpot] Syncing opportunities using strategy: lastModified {"team":2} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [Hubspot] Pagination completed {"team_id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/deals/search","total_requests":1,"total_records_fetched":0,"total_elapsed_seconds":0.28,"average_seconds_per_request":0.28} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [HubSpot] Synced opportunities {"team":2,"strategies":"lastModified","sync_count":0,"total":0,"last_synced_id":null,"duration_ms":286.56} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"abae74b8-bfa8-4383-9a7f-89f4bf2bdbb4","provider":"hubspot","status":"completed","duration_ms":1417.45,"usage":25188000,"real_usage":65011712,"pid":42210} {"correlation_id":"84144bb1-43a6-40fd-9a9c-0b259220caa0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","usage":25162720,"real_usage":65011712,"pid":42210} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"33e34a7a-1c02-4f04-87ac-22c3a385e6e3","account":{"Jiminny\\Models\\SocialAccount":{"id":306,"sociable_id":109,"provider_user_id":"11348452","expires":1701077403,"refresh_token_expires":null,"provider":"hubspot","state":"full-refresh","auth_scope":null,"retry_after":null,"created_at":"2020-09-01 16:59:04","updated_at":"2023-11-27 09:30:03"}}} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":109,"team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":29} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2b115eb-93ce-4d1b-929c-173757df8fba","provider":"hubspot","status":"disconnected","duration_ms":28.83,"usage":25162928,"real_usage":65011712,"pid":42210,"reason":"Your HubSpot account has become disconnected. Please login to Jiminny to reconnect."} {"correlation_id":"d16fc90d-2daf-4985-a507-10c03db9daf0","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","usage":25120440,"real_usage":65011712,"pid":42210} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"2ac0447f-3c8c-4ce0-baeb-b63ddb76fa9b","account":null} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":130,"team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":42} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"b2d49a54-b645-4637-a7ae-a86cfce6e8e4","provider":"hubspot","status":"disconnected","duration_ms":50.47,"usage":25140104,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"69d51f2f-966d-4ce3-a3b0-13db04d24441","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Starting sync {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","usage":25100792,"real_usage":65011712,"pid":42210} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.WARNING: [HubSpot] Account not connected for user {"userId":"71e3aac5-fb66-47c5-a236-2d051ae3e319","account":null} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"hubspot","crm_owner":256,"team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"hubspot","team_id":49} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: [SyncHubspotObjects] Sync finished {"team":"c6b9d6b0-b48d-4832-a68c-a57d60651888","provider":"hubspot","status":"disconnected","duration_ms":61.91,"usage":25136552,"real_usage":65011712,"pid":42210,"reason":"Social account for HubSpot cannot be found. Please login to Jiminny to connect."} {"correlation_id":"27759f57-4cfe-4fd0-a357-ccb54433120b","trace_id":"15667b2a-219a-4ecf-8397-f8b5729ba263"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:15] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"activity:notify-not-logged","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"8adbd852-a071-43d7-9393-80d2ceb0461e","trace_id":"f2fefab6-f987-4a8d-8ba9-98e358be3479"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] STARTING Inbox Sync {"host":"docker_lamp_1"} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: [EmailSchedule] FINISHED Inbox Sync {"host":"docker_lamp_1","events":2} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:19] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:sync","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":62.0,"memoryPeakBeforeCommandInMb":99.883,"memoryPeakAfterCommandInMB":99.883} {"correlation_id":"1ef33eb4-1aeb-441d-bee8-259627a93960","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Inbox service] Skipping METADATA SYNC for inbox 59 due to unauthorized access to the mailbox {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":59} {"correlation_id":"cf9cc0f9-72df-468a-9dfd-7419e132cd3c","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync start {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.WARNING: [Salesforce] Account not connected for user {"userId":"641f1acb-16b8-42d1-8726-df52979dad0e","account":{"Jiminny\\Models\\SocialAccount":{"id":1500,"sociable_id":143,"provider_user_id":"0052g000003frelAAA","expires":null,"refresh_token_expires":null,"provider":"salesforce","state":"full-refresh","auth_scope":"refresh_token web api","retry_after":null,"created_at":"2026-02-06 08:39:03","updated_at":"2026-05-14 09:46:30"}}} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] Integration owner is not connected, attempting team members {"crm_provider":"salesforce","crm_owner":143,"team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team members found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [CrmOwnerResolver] No team member found with active crm connection {"crm_provider":"salesforce","team_id":1} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.ERROR: Failed to set service context for google: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}
[2026-05-27 12:56:20] local.INFO: [Sync Mailbox] Sync complete {"inbox_id":212} {"correlation_id":"2ebe7131-99ad-40ee-88ce-054a8755ea10","trace_id":"566f32c7-b8d7-4e45-800e-124b5da5319e"}...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78718
|
2763
|
23
|
2026-05-27T12:56:32.025519+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886592025_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
16
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;
}
$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
6
Previous Highlighted Error
Next Highlighted Error
"dealstage": {
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"source": "CRM_UI",
"sourceId": "userId:71523846",
"updatedByUserId": 71523846,
"versions": [
{
"name": "dealstage",
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"sourceId": "userId:71523846",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019e06c6-cd38-76f4-ae81-2d85ca04a72c",
"updatedByUserId": 71523846,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "InboundDbObjects-Service-web"
},
{
"name": "dealstage",
"value": "735341516",
"timestamp": 1773305812311, # 2026-03-12 08:56:52
"sourceId": "userId:76091797",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019ce143-5502-7857-86c1-e4699e3d745d",
"updatedByUserId": 76091797,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "CrmObjectBuilderRpcServer-userweb"
}
]
},
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder...
|
[{"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":"16","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\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\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":"6","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":"\"dealstage\": {\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"source\": \"CRM_UI\",\n \"sourceId\": \"userId:71523846\",\n \"updatedByUserId\": 71523846,\n \"versions\": [\n {\n \"name\": \"dealstage\",\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"sourceId\": \"userId:71523846\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019e06c6-cd38-76f4-ae81-2d85ca04a72c\",\n \"updatedByUserId\": 71523846,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"InboundDbObjects-Service-web\"\n },\n {\n \"name\": \"dealstage\",\n \"value\": \"735341516\",\n \"timestamp\": 1773305812311, # 2026-03-12 08:56:52\n \"sourceId\": \"userId:76091797\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019ce143-5502-7857-86c1-e4699e3d745d\",\n \"updatedByUserId\": 76091797,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"CrmObjectBuilderRpcServer-userweb\"\n }\n ]\n},","depth":4,"on_screen":true,"value":"\"dealstage\": {\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"source\": \"CRM_UI\",\n \"sourceId\": \"userId:71523846\",\n \"updatedByUserId\": 71523846,\n \"versions\": [\n {\n \"name\": \"dealstage\",\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"sourceId\": \"userId:71523846\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019e06c6-cd38-76f4-ae81-2d85ca04a72c\",\n \"updatedByUserId\": 71523846,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"InboundDbObjects-Service-web\"\n },\n {\n \"name\": \"dealstage\",\n \"value\": \"735341516\",\n \"timestamp\": 1773305812311, # 2026-03-12 08:56:52\n \"sourceId\": \"userId:76091797\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019ce143-5502-7857-86c1-e4699e3d745d\",\n \"updatedByUserId\": 76091797,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"CrmObjectBuilderRpcServer-userweb\"\n }\n ]\n},","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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qai.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_staging.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_subenv.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Acl, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activity, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dtos, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AWS, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Cache, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Country, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Database, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Datadog, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DateTime, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Configuration, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Console, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Commands, folder","depth":9,"on_screen":false,"role_description":"text"}]...
|
-3361597453849066527
|
-6006514282509727350
|
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
16
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;
}
$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
6
Previous Highlighted Error
Next Highlighted Error
"dealstage": {
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"source": "CRM_UI",
"sourceId": "userId:71523846",
"updatedByUserId": 71523846,
"versions": [
{
"name": "dealstage",
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"sourceId": "userId:71523846",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019e06c6-cd38-76f4-ae81-2d85ca04a72c",
"updatedByUserId": 71523846,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "InboundDbObjects-Service-web"
},
{
"name": "dealstage",
"value": "735341516",
"timestamp": 1773305812311, # 2026-03-12 08:56:52
"sourceId": "userId:76091797",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019ce143-5502-7857-86c1-e4699e3d745d",
"updatedByUserId": 76091797,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "CrmObjectBuilderRpcServer-userweb"
}
]
},
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder...
|
78716
|
NULL
|
NULL
|
NULL
|
|
78717
|
2764
|
25
|
2026-05-27T12:56:31.922555+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886591922_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.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
16
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;
}
$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
6
Previous Highlighted Error
Next Highlighted Error
"dealstage": {
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"source": "CRM_UI",
"sourceId": "userId:71523846",
"updatedByUserId": 71523846,
"versions": [
{
"name": "dealstage",
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"sourceId": "userId:71523846",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019e06c6-cd38-76f4-ae81-2d85ca04a72c",
"updatedByUserId": 71523846,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "InboundDbObjects-Service-web"
},
{
"name": "dealstage",
"value": "735341516",
"timestamp": 1773305812311, # 2026-03-12 08:56:52
"sourceId": "userId:76091797",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019ce143-5502-7857-86c1-e4699e3d745d",
"updatedByUserId": 76091797,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "CrmObjectBuilderRpcServer-userweb"
}
]
},
Project
Project...
|
[{"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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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":"6","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":"\"dealstage\": {\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"source\": \"CRM_UI\",\n \"sourceId\": \"userId:71523846\",\n \"updatedByUserId\": 71523846,\n \"versions\": [\n {\n \"name\": \"dealstage\",\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"sourceId\": \"userId:71523846\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019e06c6-cd38-76f4-ae81-2d85ca04a72c\",\n \"updatedByUserId\": 71523846,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"InboundDbObjects-Service-web\"\n },\n {\n \"name\": \"dealstage\",\n \"value\": \"735341516\",\n \"timestamp\": 1773305812311, # 2026-03-12 08:56:52\n \"sourceId\": \"userId:76091797\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019ce143-5502-7857-86c1-e4699e3d745d\",\n \"updatedByUserId\": 76091797,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"CrmObjectBuilderRpcServer-userweb\"\n }\n ]\n},","depth":4,"bounds":{"left":0.43051863,"top":0.09736632,"width":0.32280585,"height":0.8818835},"on_screen":true,"lines":[{"char_start":155,"char_count":33,"bounds":{"left":0.43051863,"top":0.0,"width":0.08277926,"height":0.014365523}},{"char_start":188,"char_count":18,"bounds":{"left":0.43051863,"top":0.0,"width":0.043882977,"height":0.014365523}},{"char_start":206,"char_count":10,"bounds":{"left":0.43051863,"top":0.0,"width":0.023271276,"height":0.014365523}},{"char_start":216,"char_count":33,"bounds":{"left":0.43051863,"top":0.0,"width":0.08277926,"height":0.014365523}},{"char_start":249,"char_count":35,"bounds":{"left":0.43051863,"top":0.0,"width":0.08809841,"height":0.014365523}},{"char_start":284,"char_count":62,"bounds":{"left":0.43051863,"top":0.0,"width":0.15791224,"height":0.014365523}},{"char_start":346,"char_count":43,"bounds":{"left":0.43051863,"top":0.008778931,"width":0.1087101,"height":0.014365523}},{"char_start":389,"char_count":32,"bounds":{"left":0.43051863,"top":0.026336791,"width":0.080119684,"height":0.014365523}},{"char_start":421,"char_count":29,"bounds":{"left":0.43051863,"top":0.043894652,"width":0.07247341,"height":0.014365523}},{"char_start":450,"char_count":65,"bounds":{"left":0.43051863,"top":0.061452515,"width":0.16589096,"height":0.014365523}},{"char_start":515,"char_count":41,"bounds":{"left":0.43051863,"top":0.079010375,"width":0.10372341,"height":0.014365523}},{"char_start":556,"char_count":56,"bounds":{"left":0.43051863,"top":0.096568234,"width":0.14261968,"height":0.014365523}},{"char_start":612,"char_count":71,"bounds":{"left":0.43051863,"top":0.11412609,"width":0.18151596,"height":0.014365523}},{"char_start":683,"char_count":11,"bounds":{"left":0.43051863,"top":0.13168396,"width":0.025930852,"height":0.014365523}},{"char_start":694,"char_count":10,"bounds":{"left":0.43051863,"top":0.14924182,"width":0.023271276,"height":0.014365523}},{"char_start":704,"char_count":33,"bounds":{"left":0.43051863,"top":0.16679968,"width":0.08277926,"height":0.014365523}},{"char_start":737,"char_count":34,"bounds":{"left":0.43051863,"top":0.18435754,"width":0.08543883,"height":0.014365523}},{"char_start":771,"char_count":62,"bounds":{"left":0.43051863,"top":0.2019154,"width":0.15791224,"height":0.014365523}},{"char_start":833,"char_count":43,"bounds":{"left":0.43051863,"top":0.21947326,"width":0.1087101,"height":0.014365523}},{"char_start":876,"char_count":32,"bounds":{"left":0.43051863,"top":0.23703113,"width":0.080119684,"height":0.014365523}},{"char_start":908,"char_count":29,"bounds":{"left":0.43051863,"top":0.254589,"width":0.07247341,"height":0.014365523}},{"char_start":937,"char_count":65,"bounds":{"left":0.43051863,"top":0.27214685,"width":0.16589096,"height":0.014365523}},{"char_start":1002,"char_count":41,"bounds":{"left":0.43051863,"top":0.2897047,"width":0.10372341,"height":0.014365523}},{"char_start":1043,"char_count":56,"bounds":{"left":0.43051863,"top":0.30726257,"width":0.14261968,"height":0.014365523}}],"value":"\"dealstage\": {\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"source\": \"CRM_UI\",\n \"sourceId\": \"userId:71523846\",\n \"updatedByUserId\": 71523846,\n \"versions\": [\n {\n \"name\": \"dealstage\",\n \"value\": \"closedlost\",\n \"timestamp\": 1778230152543, # 2026-05-08 08:49:12\n \"sourceId\": \"userId:71523846\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019e06c6-cd38-76f4-ae81-2d85ca04a72c\",\n \"updatedByUserId\": 71523846,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"InboundDbObjects-Service-web\"\n },\n {\n \"name\": \"dealstage\",\n \"value\": \"735341516\",\n \"timestamp\": 1773305812311, # 2026-03-12 08:56:52\n \"sourceId\": \"userId:76091797\",\n \"source\": \"CRM_UI\",\n \"sourceVid\": [],\n \"requestId\": \"019ce143-5502-7857-86c1-e4699e3d745d\",\n \"updatedByUserId\": 76091797,\n \"useTimestampAsPersistenceTimestamp\": true,\n \"sourceUpstreamDeployable\": \"CrmObjectBuilderRpcServer-userweb\"\n }\n ]\n},","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}]...
|
-6521042414447471932
|
-6434306786813638261
|
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
16
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;
}
$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
6
Previous Highlighted Error
Next Highlighted Error
"dealstage": {
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"source": "CRM_UI",
"sourceId": "userId:71523846",
"updatedByUserId": 71523846,
"versions": [
{
"name": "dealstage",
"value": "closedlost",
"timestamp": 1778230152543, # 2026-05-08 08:49:12
"sourceId": "userId:71523846",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019e06c6-cd38-76f4-ae81-2d85ca04a72c",
"updatedByUserId": 71523846,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "InboundDbObjects-Service-web"
},
{
"name": "dealstage",
"value": "735341516",
"timestamp": 1773305812311, # 2026-03-12 08:56:52
"sourceId": "userId:76091797",
"source": "CRM_UI",
"sourceVid": [],
"requestId": "019ce143-5502-7857-86c1-e4699e3d745d",
"updatedByUserId": 76091797,
"useTimestampAsPersistenceTimestamp": true,
"sourceUpstreamDeployable": "CrmObjectBuilderRpcServer-userweb"
}
]
},
Project
Project...
|
78715
|
NULL
|
NULL
|
NULL
|
|
78716
|
2763
|
22
|
2026-05-27T12:56:30.273552+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886590273_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.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
PhpStormFileEditViewNavigateCodeRefactorRunToolsGitWindowHelpDOCKER (doc)screenpipe"DOCKER₴81DEV (docker)₴2-zsh88311DOCKER (docker-compose)docker_Lamp_1Dispatcheddocker_lamp_14 HubSpot sync jobs2sDONEdocker_lamp_12>&1docker_lamp_12026-05-27 12:56:13 Running ['artisan' activity:notify-not-logged]-05-27 12:56:13Jiminny\Jobs\Crm\SyncHubspot0bjectsdocker_lamp_12026-05-27 12:56:15Jiminny\Jobs\Crm\SyncHubspotObjects1:ONEdocker_lamp_12026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjectsRIINGdocker_lamp_12026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjects37.33m:ONEdocker_lamp_12026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjectsRIINGdocker_lamp_12026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjectsONE58.23msdocker_1amp_12026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjectsRIINGdocker_lamp_112026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjects70.36m:ONEdocker_lamp_1docker_lamp_1. 2s DONE• '/usr/local/bin/php' 'artisan'activity:notify-not-logged> '/proc/1,docker_1amp_12026-05-27 12:56:15 Running['artisan' activity:status-count]:24, "completed" :1, "scheduled" :1}.docker_lamp_11 '/usr/local/bin/php' 'artisan'activity:status-count › */proc/1/fd/1docker_lamp_1forsync.docker_lamp_1docker_lamp_1docker_lamp_1docker_lamp_1docker_1amp_1INGdocker_lamp_1ONEdocker_lamp_1INGdocker_lamp_1ONE2026-05-27 12:56:17 Running ['artisan' mailbox:sync] Queueing 2 inbox(‹1S DONE'/usr/local/bin/php' 'artisan' mailbox:sync › '/proc/1/fd/1' 2>&1run_artisan_schedule: Done waitingforschedule:run2026-05-27 12:56:20 Jiminny\Jobs\Mailbox|SyncInboxRI2026-05-27 12:56:20 Jiminny\Jobs\Mailbox\SyncInbox96.21ms2026-05-27 12:56:20 Jiminny\Jobs\Mailbox|SyncInboxRI2026-05-27 12:56:20 Jiminny \Jobs \Mailbox \Syncinbox90.40m:View in Docker Desktop@ View ConfigView Logsw Enable Watchd DetachHomeDMsActivityFilesLaterMore+→Jiminny ...# generar# happy_birthday# jbu-team-info# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...° Direct messagesJea Todor Stamatov *Ro Steliyan GeorgievVes&. Mira. Nikolay Yankovo Stoyan TomovP. Galya Dimitrovaf. Aneliya Angelovad James Graham&e Lukas Kovali... "Yl:: AppsToastJira Cloud100% <78• Wed 27 May 15:56:30Describe what you are looking forTodor Stamatov6 0MessagesкьдеAdd canvas@ Files+Today ~Todor Stamatovна локалната средаго забелязахLukas Kovalik ** 1:52 PMдаже не знаех че може локалноTodor Stamatov ** 1:53 PMмоят работи щото съм го настроил в .env скреденшьлите от stagingGOOGLE_TEXT_RELAY_MAILBOX=catch-allGOOGLE_TEXT_RELAY_HOST=txt.stagingjiminny.comGOOGLE_TEXT_RELAY_TOPIC=projects/jiminny-web/topics/com.jiminny.staging.txtGOOGLE_TEXT_RELAY_SUBSCRIPTION=projects/jiminny-web/subscriptions/staging-relayLukas Kovalik "Y 1:53 PMаха, интересно не съм се сетил да го тествамтакадай един lіke да го качаTodor Stamatov * 1:55 PMГОТОВОLukas Kovalik"1:56 PMмерсиTodor Stamatov has paused their notificationsMessage Todor Stamatov " Vacationing+...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78715
|
2764
|
24
|
2026-05-27T12:56:30.378622+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886590378_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.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
16
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;
}
$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;
}
}...
|
[{"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.37632978,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.3856383,"top":0.10055866,"width":0.009640957,"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\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\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}]...
|
707116212969285028
|
-6438808257202583157
|
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
16
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;
}
$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;
}
}...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78714
|
2763
|
21
|
2026-05-27T12:56:28.711786+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886588711_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.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
16
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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file...
|
[{"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":"16","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\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\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},{"role":"AXTextArea","text":"","depth":4,"on_screen":true,"value":"","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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qai.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"}]...
|
840005759593682776
|
-5281453524931671669
|
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
16
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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file...
|
78712
|
NULL
|
NULL
|
NULL
|
|
78713
|
2764
|
23
|
2026-05-27T12:56:27.823861+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886587823_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
16
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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder...
|
[{"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.31848404,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"16","depth":4,"bounds":{"left":0.32779256,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.33909574,"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.3464096,"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\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\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.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":"AXTextArea","text":"","depth":4,"bounds":{"left":0.3726729,"top":0.0726257,"width":0.3806516,"height":0.9066241},"on_screen":true,"value":"","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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"}]...
|
6045503250722312624
|
-5281383151892526197
|
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
16
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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder...
|
78711
|
NULL
|
NULL
|
NULL
|
|
78712
|
2763
|
20
|
2026-05-27T12:56:24.791526+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886584791_m1.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
15
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);
$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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder
Calendars, folder
Crm, folder
Hubspot, folder
IntegrationApp, folder
Traits, folder
AddLayoutEntities.php
AutologDelayedCommand.php
BackfillOpportunityUserFromAccountCommand.php
BullhornCommandAbstract.php
BullhornPingCommand.php
BullhornSearchCommand.php
BullhornSessionCommand.php
CheckActivityLoggableCommand.php
CleanDuplicateFieldDataCommand.php
FullSyncOpportunityCommand.php
LogActivitiesCommand.php
ManageSyncStrategyCommand.php
MatchCrmObjectsCommand.php
MatchOpportunityActivitiesCommand.php
MigrateProvider.php
ProcessHubspotObjectsSyncBatches.php
PurgeDeletedOpportunitiesCommand.php
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php, class
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
TextRelay
SyncMailbox.php, class
WatchMailboxEvents.php, class
BatchCreate.php, class
BatchFailStalled.php, class
BatchProcess.php, class
BatchRetryFailed.php, class
DeleteEmailMessagesCommand.php, final class
DeleteEmailMessagesWithoutActivityCommand.php, final class
DeleteInboxEmailsCommand.php, final class
InboxCreate.php, class...
|
[{"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":"15","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 \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\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 \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\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":true,"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":"AXTextArea","text":"","depth":4,"on_screen":true,"value":"","role_description":"text entry area","is_enabled":true,"is_focused":false,"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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qai.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_staging.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_subenv.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Acl, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activity, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dtos, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AWS, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Cache, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Country, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Database, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Datadog, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DateTime, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Configuration, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Console, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Commands, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activities, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Analytics, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Calendars, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Crm, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot, folder","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationApp, folder","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Traits, folder","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AddLayoutEntities.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AutologDelayedCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BackfillOpportunityUserFromAccountCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornCommandAbstract.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornPingCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSearchCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSessionCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CheckActivityLoggableCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CleanDuplicateFieldDataCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FullSyncOpportunityCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LogActivitiesCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ManageSyncStrategyCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MatchCrmObjectsCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MatchOpportunityActivitiesCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MigrateProvider.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProcessHubspotObjectsSyncBatches.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PurgeDeletedOpportunitiesCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ResetGovernorLimits.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SendNotLogged.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupActivityTypeForFollowUp.php, final class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupCloseCrm.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupCopperCrm.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupCrmCommand.php, abstract class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupLayouts.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncAccount.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncContact.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncFieldMetadata.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotActiveDeals.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotObjects.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncLead.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncObjects.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunitiesMissingFieldDataCommand.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunity.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncProfileMetadata.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncTeamMetadata.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"UpdateOpportunitySpecifications.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dev","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dialers","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DTOs","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Elasticsearch","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"EngagementStats","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"GeckoExport","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Livestream","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Mailboxes","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TextRelay","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncMailbox.php, class","depth":12,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"WatchMailboxEvents.php, class","depth":12,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BatchCreate.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BatchFailStalled.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BatchProcess.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BatchRetryFailed.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DeleteEmailMessagesCommand.php, final class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DeleteEmailMessagesWithoutActivityCommand.php, final class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DeleteInboxEmailsCommand.php, final class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"InboxCreate.php, class","depth":11,"on_screen":false,"role_description":"text"}]...
|
-4807826498252205439
|
-5790342599907702397
|
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
15
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);
$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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder
Calendars, folder
Crm, folder
Hubspot, folder
IntegrationApp, folder
Traits, folder
AddLayoutEntities.php
AutologDelayedCommand.php
BackfillOpportunityUserFromAccountCommand.php
BullhornCommandAbstract.php
BullhornPingCommand.php
BullhornSearchCommand.php
BullhornSessionCommand.php
CheckActivityLoggableCommand.php
CleanDuplicateFieldDataCommand.php
FullSyncOpportunityCommand.php
LogActivitiesCommand.php
ManageSyncStrategyCommand.php
MatchCrmObjectsCommand.php
MatchOpportunityActivitiesCommand.php
MigrateProvider.php
ProcessHubspotObjectsSyncBatches.php
PurgeDeletedOpportunitiesCommand.php
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php, class
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
TextRelay
SyncMailbox.php, class
WatchMailboxEvents.php, class
BatchCreate.php, class
BatchFailStalled.php, class
BatchProcess.php, class
BatchRetryFailed.php, class
DeleteEmailMessagesCommand.php, final class
DeleteEmailMessagesWithoutActivityCommand.php, final class
DeleteInboxEmailsCommand.php, final class
InboxCreate.php, class...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78711
|
2764
|
22
|
2026-05-27T12:56:24.686752+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886584686_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
15
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);
$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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder...
|
[{"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.31848404,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"15","depth":4,"bounds":{"left":0.32779256,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.33909574,"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.3464096,"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 \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\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 \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\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":true,"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":"AXTextArea","text":"","depth":4,"bounds":{"left":0.3726729,"top":0.0726257,"width":0.3806516,"height":0.9066241},"on_screen":true,"value":"","role_description":"text entry area","is_enabled":true,"is_focused":false,"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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qai.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_staging.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_subenv.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Acl, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activity, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dtos, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AWS, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Cache, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Country, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Database, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Datadog, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DateTime, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Configuration, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Console, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Commands, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activities, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Analytics, folder","depth":10,"on_screen":false,"role_description":"text"}]...
|
-2927315479835142946
|
-5862400211125433973
|
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
15
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);
$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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78710
|
2764
|
21
|
2026-05-27T12:56:23.758597+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886583758_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
15
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);
$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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder
Calendars, folder
Crm, folder
Hubspot, folder
IntegrationApp, folder
Traits, folder
AddLayoutEntities.php
AutologDelayedCommand.php
BackfillOpportunityUserFromAccountCommand.php
BullhornCommandAbstract.php
BullhornPingCommand.php
BullhornSearchCommand.php
BullhornSessionCommand.php
CheckActivityLoggableCommand.php
CleanDuplicateFieldDataCommand.php
FullSyncOpportunityCommand.php
LogActivitiesCommand.php
ManageSyncStrategyCommand.php
MatchCrmObjectsCommand.php
MatchOpportunityActivitiesCommand.php
MigrateProvider.php
ProcessHubspotObjectsSyncBatches.php
PurgeDeletedOpportunitiesCommand.php
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php, class
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class...
|
[{"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.31848404,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"15","depth":4,"bounds":{"left":0.32779256,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.33909574,"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.3464096,"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 \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\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 \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\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":true,"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":"AXTextArea","text":"","depth":4,"bounds":{"left":0.3726729,"top":0.0726257,"width":0.3806516,"height":0.9066241},"on_screen":true,"value":"","role_description":"text entry area","is_enabled":true,"is_focused":false,"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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qai.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_staging.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_subenv.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Acl, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activity, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dtos, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AWS, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Cache, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Country, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Database, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Datadog, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DateTime, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Configuration, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Console, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Commands, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activities, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Analytics, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Calendars, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Crm, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot, folder","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationApp, folder","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Traits, folder","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AddLayoutEntities.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AutologDelayedCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BackfillOpportunityUserFromAccountCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornCommandAbstract.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornPingCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSearchCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSessionCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CheckActivityLoggableCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CleanDuplicateFieldDataCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FullSyncOpportunityCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LogActivitiesCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ManageSyncStrategyCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MatchCrmObjectsCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MatchOpportunityActivitiesCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MigrateProvider.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProcessHubspotObjectsSyncBatches.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PurgeDeletedOpportunitiesCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ResetGovernorLimits.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SendNotLogged.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupActivityTypeForFollowUp.php, final class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupCloseCrm.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupCopperCrm.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupCrmCommand.php, abstract class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupLayouts.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncAccount.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncContact.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncFieldMetadata.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotActiveDeals.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotObjects.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncLead.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncObjects.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunitiesMissingFieldDataCommand.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunity.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncProfileMetadata.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncTeamMetadata.php, class","depth":11,"on_screen":false,"role_description":"text"}]...
|
3319247504857610974
|
-5790342599874147965
|
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
15
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);
$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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder
Calendars, folder
Crm, folder
Hubspot, folder
IntegrationApp, folder
Traits, folder
AddLayoutEntities.php
AutologDelayedCommand.php
BackfillOpportunityUserFromAccountCommand.php
BullhornCommandAbstract.php
BullhornPingCommand.php
BullhornSearchCommand.php
BullhornSessionCommand.php
CheckActivityLoggableCommand.php
CleanDuplicateFieldDataCommand.php
FullSyncOpportunityCommand.php
LogActivitiesCommand.php
ManageSyncStrategyCommand.php
MatchCrmObjectsCommand.php
MatchOpportunityActivitiesCommand.php
MigrateProvider.php
ProcessHubspotObjectsSyncBatches.php
PurgeDeletedOpportunitiesCommand.php
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php, class
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class...
|
78707
|
NULL
|
NULL
|
NULL
|
|
78709
|
2763
|
19
|
2026-05-27T12:56:23.305919+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886583305_m1.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
15
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);
$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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder
Calendars, folder
Crm, folder
Hubspot, folder
IntegrationApp, folder
Traits, folder
AddLayoutEntities.php
AutologDelayedCommand.php
BackfillOpportunityUserFromAccountCommand.php
BullhornCommandAbstract.php
BullhornPingCommand.php
BullhornSearchCommand.php
BullhornSessionCommand.php
CheckActivityLoggableCommand.php
CleanDuplicateFieldDataCommand.php
FullSyncOpportunityCommand.php
LogActivitiesCommand.php
ManageSyncStrategyCommand.php
MatchCrmObjectsCommand.php
MatchOpportunityActivitiesCommand.php
MigrateProvider.php
ProcessHubspotObjectsSyncBatches.php
PurgeDeletedOpportunitiesCommand.php
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php, class
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
TextRelay
SyncMailbox.php, class
WatchMailboxEvents.php, class
BatchCreate.php, class...
|
[{"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":"15","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 \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\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 \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\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":true,"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":"AXTextArea","text":"","depth":4,"on_screen":true,"value":"","role_description":"text entry area","is_enabled":true,"is_focused":false,"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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qai.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_staging.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_subenv.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Acl, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activity, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dtos, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AWS, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Cache, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Country, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Database, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Datadog, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DateTime, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Configuration, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Console, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Commands, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activities, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Analytics, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Calendars, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Crm, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot, folder","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationApp, folder","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Traits, folder","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AddLayoutEntities.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AutologDelayedCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BackfillOpportunityUserFromAccountCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornCommandAbstract.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornPingCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSearchCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSessionCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CheckActivityLoggableCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CleanDuplicateFieldDataCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FullSyncOpportunityCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LogActivitiesCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ManageSyncStrategyCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MatchCrmObjectsCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MatchOpportunityActivitiesCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MigrateProvider.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProcessHubspotObjectsSyncBatches.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PurgeDeletedOpportunitiesCommand.php","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ResetGovernorLimits.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SendNotLogged.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupActivityTypeForFollowUp.php, final class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupCloseCrm.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupCopperCrm.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupCrmCommand.php, abstract class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SetupLayouts.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncAccount.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncContact.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncFieldMetadata.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotActiveDeals.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncHubspotObjects.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncLead.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncObjects.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunitiesMissingFieldDataCommand.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncOpportunity.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncProfileMetadata.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncTeamMetadata.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"UpdateOpportunitySpecifications.php, class","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dev","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dialers","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DTOs","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Elasticsearch","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"EngagementStats","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"GeckoExport","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Livestream","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Mailboxes","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"TextRelay","depth":11,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"SyncMailbox.php, class","depth":12,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"WatchMailboxEvents.php, class","depth":12,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BatchCreate.php, class","depth":11,"on_screen":false,"role_description":"text"}]...
|
-5490749124591502881
|
-5790342599916091005
|
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
15
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);
$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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder
Calendars, folder
Crm, folder
Hubspot, folder
IntegrationApp, folder
Traits, folder
AddLayoutEntities.php
AutologDelayedCommand.php
BackfillOpportunityUserFromAccountCommand.php
BullhornCommandAbstract.php
BullhornPingCommand.php
BullhornSearchCommand.php
BullhornSessionCommand.php
CheckActivityLoggableCommand.php
CleanDuplicateFieldDataCommand.php
FullSyncOpportunityCommand.php
LogActivitiesCommand.php
ManageSyncStrategyCommand.php
MatchCrmObjectsCommand.php
MatchOpportunityActivitiesCommand.php
MigrateProvider.php
ProcessHubspotObjectsSyncBatches.php
PurgeDeletedOpportunitiesCommand.php
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class
SyncHubspotActiveDeals.php, class
SyncHubspotObjects.php, class
SyncLead.php, class
SyncObjects.php, class
SyncOpportunitiesMissingFieldDataCommand.php, class
SyncOpportunity.php, class
SyncProfileMetadata.php, class
SyncTeamMetadata.php, class
UpdateOpportunitySpecifications.php, class
DealInsights
Dev
Dialers
DTOs
Elasticsearch
EngagementStats
GeckoExport
Livestream
Mailboxes
TextRelay
SyncMailbox.php, class
WatchMailboxEvents.php, class
BatchCreate.php, class...
|
78708
|
NULL
|
NULL
|
NULL
|
|
78708
|
2763
|
18
|
2026-05-27T12:56:20.309283+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886580309_m1.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
15...
|
[{"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":"15","depth":4,"on_screen":true,"role_description":"text"}]...
|
-7625260430145728844
|
-5317255795552738366
|
typing_pause
|
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
Code changed:
Hide
Sync Changes
Hide This Notification
1
15
PhpStormFileEditViewNavigateCodeRefactorRunToolsGitWindowHelpDOCKER (doc)DOCKER₴8111DOCKER (docker-compose)docker_lamp_1skipping...docker_lamp_1skipping...docker_lamp_1DEV (docker)₴2-zsh883screenpipe'I Team test (7997eb70-8aa4-491a-870d-311977568df4) is not yet assigned an ‹1 Team Test (Se06dcee-0613-470e-9a77-2c283198f3bf) is not yet assigned an ‹I Team test ogg auto sync (da44776e-306f-427a-83d8-a1b4baa5537e) is not ye•docker_lamp_1I Team Tourlaner (d9b71080-388b-4cf5-8175-aa0f29bee635) is not yet assignedn owner.skipping...docker_lamp_1I Dispatched 4 HubSpot sync jobsdocker_lamp_12S DONEdocker_lamp_1l '/usr/local/bin/php' 'artisan' crm:sync-hubspot-objects › */proc/1/fc2>&1docker_lamp_12026-05-27 12:56:13 Running ['artisan' activity:notify-not-logged]-05-27 12:56:13Jiminny\Jobs\Crm\SyncHubspotObjects20docker_lamp_12026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjects13ONEdocker_1amp_12026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjectsRIdocker_lamp_12026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjects37.33m:docker_lamp_1INGdocker_lamp_1ONEdocker_lamp_12026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjectsRI2026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjects58.23m;2026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjectsdocker_lamp_12026-05-27 12:56:15 Jiminny\Jobs\Crm\SyncHubspotObjectsdocker_lamp_1docker_lamp_11. 2s DONEl '/usr/local/bin/php' 'artisan' activity:notify-not-logged › '/proc/1,docker_lamp_12026-05-27 12:56:15 Running ['artisan' activity:status-count] {"cancel‹:24, "completed" :1,"scheduled":13......docker_1amp_1• '/usr/local/bin/php' 'artisan' activity:status-count > */proc/1/fd/1docker_lamp_12026-05-27 12:56:17 Running ['artisan'mailbox: sync]docker_lamp_1docker_lamp_1docker_lamp_1docker_lamp_1'/usr/local/bin/php' 'artisan' mailbox:sync › '/proc/1/fd/1' 2>&1run_artisan_schedule: Done waiting for schedule:runView in Docker Desktop@ View ConfigL View Logsw Enable WatchDetachHomeDMsActivityFilesLater...More+→Jiminny ...# generar# happy_birthday# jbu-team-info# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...° Direct messagesJea Todor Stamatov *Ro Steliyan GeorgievVes&. Mira. Nikolay Yankovo Stoyan TomovP. Galya Dimitrovaf. Aneliya Angelovad James GrahamEe Lukas Kovali... "l:: AppsToastJira Cloud100% <78• Wed 27 May 15:56:20Describe what you are looking forTodor Stamatov6 0MessagesкьдеAdd canvas@ Files+Today ~Todor Stamatovна локалната средаго забелязахLukas Kovalik ** 1:52 PMдаже не знаех че може локалноTodor Stamatov ** 1:53 PMмоят работи щото съм го настроил в .env скреденшьлите от stagingGOOGLE_TEXT_RELAY_MAILBOX=catch-allGOOGLE_TEXT_RELAY_HOST=txt.stagingjiminny.comGOOGLE_TEXT_RELAY_TOPIC=projects/jiminny-web/topics/com.jiminny.staging.txtGOOGLE_TEXT_RELAY_SUBSCRIPTION=projects/jiminny-web/subscriptions/staging-relayLukas Kovalik "Y 1:53 PMаха, интересно не съм се сетил да го тествамтакадай един like да го качаTodor Stamatov * 1:55 PMГОТОВОLukas Kovalik"1:56 PMмерсиTodor Stamatov has paused their notificationsMessage Todor Stamatov " Vacationing+...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78707
|
2764
|
20
|
2026-05-27T12:56:20.414495+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886580414_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
15
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);
;
$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;
}
$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...
|
[{"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.31848404,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"15","depth":4,"bounds":{"left":0.32779256,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.33909574,"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.3464096,"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 ;\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\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 ;\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\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":true,"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}]...
|
3241116584390096980
|
-6438808257208874613
|
typing_pause
|
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
15
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);
;
$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;
}
$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...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
78706
|
2763
|
17
|
2026-05-27T12:56:18.741033+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886578741_m1.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
15
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);
$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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder...
|
[{"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":"15","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 $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\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 $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\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":true,"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":"AXTextArea","text":"","depth":4,"on_screen":true,"value":"","role_description":"text entry area","is_enabled":true,"is_focused":false,"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},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"backend-code, folder","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".env.staging","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dockerfile","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"start.sh","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"frontend-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"worker-video-code","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"config_continue.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"subenv_to_branch_map","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_prod.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qa.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_qai.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_staging.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"workflows_subenv.yml, YAML file","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Acl, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Activity, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Dtos, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"AWS, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Cache, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Country, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Database, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Datadog, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DateTime, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"on_screen":false,"role_description":"text"}]...
|
7192409499389410133
|
-5862400211662042741
|
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
15
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);
$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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
backend-code, folder
.env.staging
Dockerfile
start.sh
frontend-code
worker-code
worker-video-code
config.yml, YAML file
config_continue.yml, YAML file
subenv_to_branch_map
workflows_prod.yml, YAML file
workflows_qa.yml, YAML file
workflows_qai.yml, YAML file
workflows_staging.yml, YAML file
workflows_subenv.yml, YAML file
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder...
|
78704
|
NULL
|
NULL
|
NULL
|
|
78705
|
2764
|
19
|
2026-05-27T12:56:18.638693+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-27/1779 /Users/lukas/.screenpipe/data/data/2026-05-27/1779886578638_m2.jpg...
|
PhpStorm
|
faVsco.js – TextRelayService.php
|
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
15
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);
$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;
}
$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
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.31848404,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"15","depth":4,"bounds":{"left":0.32779256,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.33909574,"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.3464096,"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 $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\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 $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\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":true,"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":"AXTextArea","text":"","depth":4,"bounds":{"left":0.3726729,"top":0.0726257,"width":0.3806516,"height":0.9066241},"on_screen":true,"value":"","role_description":"text entry area","is_enabled":true,"is_focused":false,"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}]...
|
-3087218679761999735
|
-5862347504905450613
|
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
15
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);
$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;
}
$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
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
78702
|
NULL
|
NULL
|
NULL
|