|
53065
|
1858
|
19
|
2026-05-18T11:32:53.268585+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103973268_m1.jpg...
|
PhpStorm
|
faVsco.js – TextMessagingService.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82X t1DOCKER (docker-compose)kibana{"type""log""@timestamp": "2026-05-18T11:32:48Z","tags" : ["error""elasticsearch", "data"],"pid" :7,"message":"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}kibana1 {"type" : "log""@timestamp" : "2026-05-18T11:32:48Z", "tags" : ["warning".asticsearch", "data"],"pid" :7,',"el"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:32:48Z", "tags" : ["warning"asticsearch", "data"],"pid" :7,"message"kibana1 {"type" : "log"connections"},"@timestamp""2026-05-18T11:32:48Z""tags" : ["error"ins",, "taskManager"Living connections"}"taskManager"], "pid":7,"message":"Failed to poll for work: Error: Nokibana1 {"type" : "log","@timestamp": "2026-05-18T11:32:49Z", "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message" : "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:32:49Z","tags" : ["warning", "elasticsearch", "data"], "pid":7, "message":"No livingconnections"}kibana1 {"type":"loq"ugins","licensing"],,"@timestamp" : "2026-05-18T11:32:49Z","tags" : ["warning""pid":7, "message" :"License information could not be obtained from Elasticsearch due to Error: No Living connections error"}kibana1 {"type": "log","@timestamp" : "2026-05-18T11:32:49Z", "tags" : ["warning"asticsearch", "monitoring"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "tags" : ["warning", "elasticsearch", "monitoring"], "pid" :7, "message" : "No living connections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:32:497""tags" : ["warning", "pl,"licensing"], "pid" :7, "message" : "License information could not be obtained from Elasticsearch due to Error: No Living connections error"}1 {"type" : "log","@timestamp" : "2026-05-18T11:32:50Z", "tags" : ["error"ticsearch",, "data"], "pid":7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch: 9200"}I {"type" : "log", "@timestamp" : "2026-05-18T11:32:50Z""tags" : ["error", "elas, "data"], "pid":7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}1 {"type" : "log","@timestamp": "2026-05-18T11:32:51Z""tags": ["warning"asticsearch", "data"], "pid" :7,"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:32:51Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7,"message": "No living connections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:32:51Z","tags" : ["error", "plugins",, "taskManager",, "taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View ConfigEnable Watch8• Mon 18 May 14:32:526 0Jiminny ...MetromrenCE# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario GeorgievNikolay IvanovLo James Graham *. Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 28 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:O Files+HomeDMsActivityFilesLaterMoreSign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
902031442136684026
|
NULL
|
click
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82X t1DOCKER (docker-compose)kibana{"type""log""@timestamp": "2026-05-18T11:32:48Z","tags" : ["error""elasticsearch", "data"],"pid" :7,"message":"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}kibana1 {"type" : "log""@timestamp" : "2026-05-18T11:32:48Z", "tags" : ["warning".asticsearch", "data"],"pid" :7,',"el"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:32:48Z", "tags" : ["warning"asticsearch", "data"],"pid" :7,"message"kibana1 {"type" : "log"connections"},"@timestamp""2026-05-18T11:32:48Z""tags" : ["error"ins",, "taskManager"Living connections"}"taskManager"], "pid":7,"message":"Failed to poll for work: Error: Nokibana1 {"type" : "log","@timestamp": "2026-05-18T11:32:49Z", "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message" : "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:32:49Z","tags" : ["warning", "elasticsearch", "data"], "pid":7, "message":"No livingconnections"}kibana1 {"type":"loq"ugins","licensing"],,"@timestamp" : "2026-05-18T11:32:49Z","tags" : ["warning""pid":7, "message" :"License information could not be obtained from Elasticsearch due to Error: No Living connections error"}kibana1 {"type": "log","@timestamp" : "2026-05-18T11:32:49Z", "tags" : ["warning"asticsearch", "monitoring"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "tags" : ["warning", "elasticsearch", "monitoring"], "pid" :7, "message" : "No living connections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:32:497""tags" : ["warning", "pl,"licensing"], "pid" :7, "message" : "License information could not be obtained from Elasticsearch due to Error: No Living connections error"}1 {"type" : "log","@timestamp" : "2026-05-18T11:32:50Z", "tags" : ["error"ticsearch",, "data"], "pid":7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch: 9200"}I {"type" : "log", "@timestamp" : "2026-05-18T11:32:50Z""tags" : ["error", "elas, "data"], "pid":7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}1 {"type" : "log","@timestamp": "2026-05-18T11:32:51Z""tags": ["warning"asticsearch", "data"], "pid" :7,"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:32:51Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7,"message": "No living connections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:32:51Z","tags" : ["error", "plugins",, "taskManager",, "taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View ConfigEnable Watch8• Mon 18 May 14:32:526 0Jiminny ...MetromrenCE# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario GeorgievNikolay IvanovLo James Graham *. Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 28 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:O Files+HomeDMsActivityFilesLaterMoreSign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53067
|
1858
|
20
|
2026-05-18T11:32:55.416869+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103975416_m1.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82{"type" : "log"asticsearch", "data"], "pid" :7,"@timestamp": "2026-05-18T11:32:49Z","tags" : ["warning"connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11: 32:49Z", "tags"["warning"asticsearch", "data"],"pid":7,"message": "No livingconnections "}I {"type" : "log", "@timestamp" : "2026-05-18T11:32:49Z""tags": ["warning'"licensing"], "pid":7, "message" : "License informationcould not be obtained fromasticsearchto Error: No Livingconnections1 {"type" : "log""@timestamp": "2026-05-18T11:32:49Z","tags" : ["warning"asticsearch", "monitoring"], "pid" :7, "message": "Unable torevive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:32:49Z" , "tags" : ["warning", "elasticsearch", "monitoring"],"pid" :7,"message": "No living connections"}1 {"type" :"1og""@timestamp": "2026-05-18T11:32:49Z""tags" : ["warning","licensing"],"message": "Licenseinformation could not be obtained from Elasticsearch due to Error: No Livingconnections1 {"type": "log""@timestamp": "2026-05-18T11:32:50Z","tags" : ["error", "elasticsearch",,"data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200*}ticsearch"1 {"type": "log"!,"@timestamp" : "2026-05-18T11:32:50Z", "tags" : ["error","data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200*}1 {"type": "log","@timestamp":"2026-05-18T11:32:51Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:32:51Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:32:51Z", "tags" : ["error", "taskManager", "taskManager"], "pid" :7, "message" : "Failed to pollfor work: Error: NoLiving connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:32:53Z""tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}1 {"type" : "log""@timestamp":"2026-05-18T11:32:54Z""tags": ["warning"asticsearch", "data"], "pid" :7, "message" : "Unableto revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:32:54Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7,"message": "No living connections"}1 {"type" : "log".,"@timestamp": "2026-05-18T11:32:54Z","tags" : ["error", "plug, "taskManager", "taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View Config•HomeDMsActivityFilesLaterMore-Jiminny ...Metrotrtencee# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario GeorgievNikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 28 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:32:556 0O Files+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
-3186508365840721323
|
NULL
|
click
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82{"type" : "log"asticsearch", "data"], "pid" :7,"@timestamp": "2026-05-18T11:32:49Z","tags" : ["warning"connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11: 32:49Z", "tags"["warning"asticsearch", "data"],"pid":7,"message": "No livingconnections "}I {"type" : "log", "@timestamp" : "2026-05-18T11:32:49Z""tags": ["warning'"licensing"], "pid":7, "message" : "License informationcould not be obtained fromasticsearchto Error: No Livingconnections1 {"type" : "log""@timestamp": "2026-05-18T11:32:49Z","tags" : ["warning"asticsearch", "monitoring"], "pid" :7, "message": "Unable torevive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:32:49Z" , "tags" : ["warning", "elasticsearch", "monitoring"],"pid" :7,"message": "No living connections"}1 {"type" :"1og""@timestamp": "2026-05-18T11:32:49Z""tags" : ["warning","licensing"],"message": "Licenseinformation could not be obtained from Elasticsearch due to Error: No Livingconnections1 {"type": "log""@timestamp": "2026-05-18T11:32:50Z","tags" : ["error", "elasticsearch",,"data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200*}ticsearch"1 {"type": "log"!,"@timestamp" : "2026-05-18T11:32:50Z", "tags" : ["error","data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200*}1 {"type": "log","@timestamp":"2026-05-18T11:32:51Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:32:51Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:32:51Z", "tags" : ["error", "taskManager", "taskManager"], "pid" :7, "message" : "Failed to pollfor work: Error: NoLiving connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:32:53Z""tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}1 {"type" : "log""@timestamp":"2026-05-18T11:32:54Z""tags": ["warning"asticsearch", "data"], "pid" :7, "message" : "Unableto revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:32:54Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7,"message": "No living connections"}1 {"type" : "log".,"@timestamp": "2026-05-18T11:32:54Z","tags" : ["error", "plug, "taskManager", "taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View Config•HomeDMsActivityFilesLaterMore-Jiminny ...Metrotrtencee# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario GeorgievNikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 28 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:32:556 0O Files+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
53065
|
NULL
|
NULL
|
NULL
|
|
53068
|
1859
|
28
|
2026-05-18T11:32:56.393508+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103976393_m2.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'...
|
[{"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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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}]...
|
-2796147080355312063
|
-8636637127315567163
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
PhpStormVIewINavigarecodeFV faVsco.js°9 master kProiect v› D ConferenceHandlerv D [EMAIL]© ConferenceCallbackHandler.phpConferenceManager.php>ODTOC7 Event>D ExceptionO JobResolveiService,DVOc)wilioconstants.onoc)TwiioReoositorv.onoUoloaden7 UriGeneratorD UtilityM UuidN WaveformN WehhooksWorkflow> M ConfiaurationD ConsoleD Commands>@ ActivitiesD) Analytics)mCnlondarevm Crm>@ Hubspot>C IntegrationAppServicesTОC 0xv M DatabasevAEUA console 4 s#liminnvAlocalhoctA SPA HS_localA PRODA console 4 sA STAGING& console, NoshorLaravelKeractorJOOISWindow© ActivityController.phpC.BaseService.onp© SoftPhoneManager.php X© TextMessagingService.phpfinal readonly class SoftPhoneManager implements ConferenceManagerprivate function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\A55305"Sorry, dialing %s is not available with your subscription.'Please contact your manager to enable additional destinations.',«values: Sregion ?? ' this number',$lead = $account = Sopportunity = $contact = $stage = null;try {$crmResolver = app( abstract: Crm0wnerResolver::class, [ream = sroomuwner->qerleamor"inteqratzonAdmin' => Sroomiwneri'providerStua => Sroombwner->qetTeamo->getcrmconfzqurationo->getProviderNameOScrmService = ScrmResolver->prepareCrmService@:slead, saccount. Soonortunitv. Scontact, Sstagel = ScrmService->matchBvPhonedScallerid.SroomOwner->aetidocatchThrowable Sthrowable)$this->logger->errorC__METHOD'exceptiof' 1i 2edirsowabl v Accept File 2+': crm exception', [X Reject File 0%@OutputGid jiminny_jupiter.users xd W 1rowvG00Xu sync emarusync dialerJ sunc conferenceJ crm reauiredim nudaes sent at<nul1>conference join reminderI created_at( updated at2024-07-04 08:09:022025-02-03 15:09:34Mactivity action itemsM slack_ follow_up=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]& console [PROD]# console [euyA console [STAGING] X556558.563564565567568569573575=577579Tx: Autovere cri couT LuuraqionE Tanu UEULaVUUUK LE WO:Go jiminny_jupiterBIS BIS XUAYselect * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'andn status = 1lAND t.status = 'active'AND (r.expires_at >= now OR r.expires_ at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;select x Tron aculvity searches where 10 = 10752select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55)results where id IN (81):select * from users where id IN (10633. 13987. 11985):|* from users where group id IN (3710):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:• suppont Dally • In 2om100% 1• Mon 18 May 14:32:56U AskJiminnyReportActivityServiceTest vCascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token ra+0 ..77 tasks doneFixed the try-catch block structure in lextmessagingservice. The issue was that the catch block tor SocialAccountTokenInvalidException (line 147) had no matching try block - the CRM usage code wasn't wrapped in a try block after I added theFinal Implementation summary:1. SoftPhoneManager.php - Added CrmOwnerResolver fallback for both outbound and inbound Twilio calls2. TextMessagingService.php - Added CrmOwnerResolver fallback for both buildActivity and convertCustomerToCousoth implementations now:•First try crmownerReso lver to tind an active salestorce token from the organization• If that tails. tall back to the oricinal direct user assianment nattern•Let existing outer catch blocks handle any SocialAccountTokeninvalidException by setting Ckv data to nullThis ensures Twilio calls will use another active Salesforce token when the admin token expires, preventing "Unknowncustomer issues.Tlle with changes yapp/Component/Twilio/Conference/ConferenceManager/D SoftPhoneManager.php +20-7Ask anvthina (%4L)+ <>Code swe.telView allReject allAccept alleSV v]W Windsurf Teams 320:50 UTF-8 # 4 spaces...
|
53066
|
NULL
|
NULL
|
NULL
|
|
53069
|
1859
|
29
|
2026-05-18T11:32:59.406752+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103979406_m2.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections...
|
[{"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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7429716278976468786
|
-8636355650190325311
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
PhpStormViewINavigareCodeLaravelRefactorJOOISWindowFV faVsco.js°9 master kProiect© ActivityController.phpC BaseService.php© SoftPhoneManager.php X>@ ConferenceHandlerv M Conterencemanage,© TextMessagingService.php© SoftPhoneManager.ong© ConferenceCallbackHandler.phpConferenceManager.php>ODTOC Event> 0 Exceptione Jobfinal readonly class SoftPhoneManager implements ConferenceManagerA9V1AVprivate function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\A55format: 'Sorry, dialing %s is not available with your subscription.'' Please contact your manager to enable additional destinations.',"Values: Sregion ??' this number',>W ResolvelW Service,DVO$lead = $account = $opportunity = $contact = $stage = null;c)wilioconstants.onoc)TwiioReoositorv.onoUoloaden7 UriGeneratortryt$crmResolver = app( abstract: Crm0wnerResolver::class, ['team' => $roomOwner->getTeam(),D UtilityM UuidN Waveformn Wehhooks'providerSlua' => SroomOwner->getTeam()->qetCrmConfiqurationO->qetProviderName@Somservice + SereResotVver-spreparecmsenvzee 0):h Workflow> M Confiauration[slead, Saccount, Sopportunity, $contact, Sstage] = ScrmService-›natchByPhone(D ConsoleScallerid.D Commands>@ ActivitiesSroomQwnen->aetIdOD) Analytics)mCnlondarevm Crm>C Hubspot329} catch (\Throwable $throwable) 1$this->logger->error C__METHOD__ . ': crm exception', t"pycention' => Sthoowahl.p› IntegrationAppD);Local ChangesShelLogChanaes 5 filesE env.local [EMAIL] app/Console/Commands< → ® Side-by-side viewer -@cb4ebf0c config/logging.phpDo not ianoreySoftPhoneManager.php app/Component/Twilio/Conference/ConferenceMar(C) TextMessacinaService.oho aoo/Services/Telephonvv Unversioned Files 9 files= env.nikillocal anniE .env.other app© CanAccessAiReportsTest.php tests/Unit/Policies@ CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Ro) favicon.ico nublidE ids.txt appTraw_sqL_query.sql [EMAIL] app/Console/Commands/Crm/HubspotM+ WEBHOOK_FILTERING_IMPLEMENTATION.md app'level' => env('LOG _LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X556565568570573-575- 576=577578579Tx: AutovTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE 1D:select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_ at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;select x Tron aculvity searches where 10 = 10752select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55results where id IN (81):select * trom users where 1d IN 00055,15787,11985011select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43) = uuid;select * from teams:• suppont Dally • In 2om100% S2• Mon 18 May 14:32:59U AskJiminnyReportActivityServiceTest v& console [PROD]& console [EU]CascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token Falli+O •Go jiminny_jupiterI see the issue - the try-catch structure is broken. The catch block on line 147 for SocialAccountTokenInvalidExceptionmatching try block because the eRM usage code uines 115-146 Is not wrapped in a try block.BISBIS XIA V• TextMessagingService.php7/7 tasks doneFixed the try-catch block structure in Textmessagingservice. The issue was that the catch block tor Socia AccountTokeninvalidException (line 147) had no matching try block - the CRM usage code wasn't wrapped in a try block after I added theFinal Implementation summary:1. SoftPhoneManager.php - Added CrmOwnerResolver fallback for both outbound and inbound Twilio calls2. TextMessagingService.php - Added CrmOwnerResolver fallback for both buildActivity and convertCustomerToCoulnerycode metnoas, with proper try-catch structureBoth imolementations now:• First try CrmOwnerResolver to find an active Salesforce token from the organization•If that fails, fall back to the original direct user assignment pattern• Let existing outer catch blocks handle any Socia AccountTokenInvalidException by settina CRM data to nullThis ensures Twilio calls will use another active Salesforce token when the admin token expires, preventing "Unknowncustomer" issues.Ask anvthina (%4L)+ @code SWE-1.6Current vercion1ditterenceMAAMO'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').W Windsurf Teams 328:49 UTF-8 P. 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53070
|
1859
|
30
|
2026-05-18T11:33:02.436525+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103982436_m2.jpg...
|
Firefox
|
[JY-20771] Call Scoring filter for Exec reports - [JY-20771] Call Scoring filter for Exec reports - Jira — Work...
|
1
|
jiminny.atlassian.net/browse/JY-20771
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Service-Desk - Queues - Platform team - Service space - Jira
Service-Desk - Queues - Platform team - Service space - Jira
Usage | Windsurf
Usage | Windsurf
Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app
Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app
Pipelines - jiminny/app
Pipelines - jiminny/app
[SRD-6848] Sidekick SMS issue - Jira
[SRD-6848] Sidekick SMS issue - Jira
CloudWatch | us-east-2
CloudWatch | us-east-2
CloudWatch | us-east-2
CloudWatch | us-east-2
Jiminny
Jiminny
Jiminny\Exceptions\SocialAccountTokenInvalidException: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. — jiminny — app
Jiminny\Exceptions\SocialAccountTokenInvalidException: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. — jiminny — app
Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app
Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app
[SRD-6862] ‘User does not have any of the necessary access rights’ when trying to see another team’s performance scored with the AI ACS in team insights. - Jira
[SRD-6862] ‘User does not have any of the necessary access rights’ when trying to see another team’s performance scored with the AI ACS in team insights. - Jira...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Service-Desk - Queues - Platform team - Service space - Jira","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Service-Desk - Queues - Platform team - Service space - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.10721409,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.15003991,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.16121309,"width":0.1796875,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Pipelines - jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.18276137,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Pipelines - jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.19393456,"width":0.039228722,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6848] Sidekick SMS issue - Jira","depth":4,"bounds":{"left":0.0,"top":0.21548285,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6848] Sidekick SMS issue - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.22665602,"width":0.06632314,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"CloudWatch | us-east-2","depth":4,"bounds":{"left":0.0,"top":0.2482043,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"CloudWatch | us-east-2","depth":5,"bounds":{"left":0.013297873,"top":0.25937748,"width":0.041223403,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"CloudWatch | us-east-2","depth":4,"bounds":{"left":0.0,"top":0.28092578,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"CloudWatch | us-east-2","depth":5,"bounds":{"left":0.013297873,"top":0.29209897,"width":0.041223403,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny","depth":4,"bounds":{"left":0.0,"top":0.31364724,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny","depth":5,"bounds":{"left":0.013297873,"top":0.32482043,"width":0.013131649,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Jiminny\\Exceptions\\SocialAccountTokenInvalidException: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. — jiminny — app","depth":4,"bounds":{"left":0.0,"top":0.3463687,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Jiminny\\Exceptions\\SocialAccountTokenInvalidException: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. — jiminny — app","depth":5,"bounds":{"left":0.013297873,"top":0.3575419,"width":0.2847407,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app","depth":4,"bounds":{"left":0.0,"top":0.3790902,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app","depth":5,"bounds":{"left":0.013297873,"top":0.39026338,"width":0.1796875,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"[SRD-6862] ‘User does not have any of the necessary access rights’ when trying to see another team’s performance scored with the AI ACS in team insights. - Jira","depth":4,"bounds":{"left":0.0,"top":0.41181165,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"[SRD-6862] ‘User does not have any of the necessary access rights’ when trying to see another team’s performance scored with the AI ACS in team insights. - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.42298484,"width":0.2835771,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
4973431620668629240
|
-3456622114664295983
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Service-Desk - Queues - Platform team - Service space - Jira
Service-Desk - Queues - Platform team - Service space - Jira
Usage | Windsurf
Usage | Windsurf
Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app
Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app
Pipelines - jiminny/app
Pipelines - jiminny/app
[SRD-6848] Sidekick SMS issue - Jira
[SRD-6848] Sidekick SMS issue - Jira
CloudWatch | us-east-2
CloudWatch | us-east-2
CloudWatch | us-east-2
CloudWatch | us-east-2
Jiminny
Jiminny
Jiminny\Exceptions\SocialAccountTokenInvalidException: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. — jiminny — app
Jiminny\Exceptions\SocialAccountTokenInvalidException: Your Salesforce account has become disconnected. Please login to Jiminny to reconnect. — jiminny — app
Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app
Allow owner's role to be selected when setting up a trial by LakyLak · Pull Request #12092 · jiminny/app
[SRD-6862] ‘User does not have any of the necessary access rights’ when trying to see another team’s performance scored with the AI ACS in team insights. - Jira
[SRD-6862] ‘User does not have any of the necessary access rights’ when trying to see another team’s performance scored with the AI ACS in team insights. - Jira...
|
53069
|
NULL
|
NULL
|
NULL
|
|
53072
|
1859
|
31
|
2026-05-18T11:33:05.536395+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103985536_m2.jpg...
|
Firefox
|
[JY-20771] Call Scoring filter for Exec reports - [JY-20771] Call Scoring filter for Exec reports - Jira — Work...
|
1
|
jiminny.atlassian.net/browse/JY-20878
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
FirefoxEditVIewMistorToolsPlatform Sprint 4 Q2 - P FirefoxEditVIewMistorToolsPlatform Sprint 4 Q2 - Platform TeService-Desk - Queues - PlatfornW Usage | WindsurfAllow owner's role to be selected© Pipelines - jiminny/appN1 (SRD-6848] Sidekick SMS issue -CloudWatch I us-east-2CloudWatch | us-east-28 Jiminny) Jiminny|Exceptions|SocialAccount*Allow owner's role to be selectedY [SRD-6862] 'User does not have a* New Tab(JY-209121 Fallback mechanism fc[UY-20771] Call Scoring filter fc X+T* (UY-20878) SCIM > Allow custome- WJY-208791 Enable users to use thi[JY-20847] Users can filter Score:(UY-20534] Al Call Scoring qui: x+ New TabbookmarksProtllesWindowmelpny.aulasslan.neu/orowse/Jt=z0//lO JIMINNY@ For you(• Recent|# Starred0+ Apps|Q Spaces+ ...Jiminny (New)ID Platform Team+ **IID Capture TeamID Enterprise Stability I...IN Processing TeamMl SE KanbanC Service-Desk= More spaces— FiltersC DashboardsC÷ Operations2 Confluence:: Teams"= Customise sidebar|Q SearchSpaces / Jiminny (New) / # sY-19309 / [ JY-20771Call Scoring filter for Exec reports~ DescriptionNow that we have the call scoring feature, customers are asking for their report to be done only on calls with certain scores (either high or low).• include the call scoring filter in the Exec Reports section in Kiosk• use the same dropdown structure that we have in OD• put it before Recipients• ensure the scores filter applied for the report when fetching the calls for aanalisysSubtasksAdd subtaskLinked work itemsAdd linked work itemActivityComments History Work logAdd a comment….Who is working on this...? Status update...Thanks…..Pro tip: press (M) to commentC< 40 0 1 Support Daily- in 27m A 100% C/3 & • Mon 18 May 14:33:05ASK ROVO A® Lộ 0+ CreateBacklog~ * Improve StoryDetailsAssigneeReporterDevelopmentComponentsSub-ProductLabelsStory point estimateStory PointsOrganisationsPriorityFix versionsSprintDaysNeed QA& UnassignedAssign to meGalya Dimitrova[ Open with VS Code33 Create branch/ Create commitPlatformAdd optionsNoneNoneNoneNone= MediumNonePlatform Sprint 5 Q2NoneParentCanny LinksAdd optionJY-19309 Automated Al ScoringOpen Canny Links> More fields Original estimate, Time tracking> Automation 4 Rule executions> featureOS Open featureOs...
|
NULL
|
5100171314853607344
|
NULL
|
visual_change
|
ocr
|
NULL
|
FirefoxEditVIewMistorToolsPlatform Sprint 4 Q2 - P FirefoxEditVIewMistorToolsPlatform Sprint 4 Q2 - Platform TeService-Desk - Queues - PlatfornW Usage | WindsurfAllow owner's role to be selected© Pipelines - jiminny/appN1 (SRD-6848] Sidekick SMS issue -CloudWatch I us-east-2CloudWatch | us-east-28 Jiminny) Jiminny|Exceptions|SocialAccount*Allow owner's role to be selectedY [SRD-6862] 'User does not have a* New Tab(JY-209121 Fallback mechanism fc[UY-20771] Call Scoring filter fc X+T* (UY-20878) SCIM > Allow custome- WJY-208791 Enable users to use thi[JY-20847] Users can filter Score:(UY-20534] Al Call Scoring qui: x+ New TabbookmarksProtllesWindowmelpny.aulasslan.neu/orowse/Jt=z0//lO JIMINNY@ For you(• Recent|# Starred0+ Apps|Q Spaces+ ...Jiminny (New)ID Platform Team+ **IID Capture TeamID Enterprise Stability I...IN Processing TeamMl SE KanbanC Service-Desk= More spaces— FiltersC DashboardsC÷ Operations2 Confluence:: Teams"= Customise sidebar|Q SearchSpaces / Jiminny (New) / # sY-19309 / [ JY-20771Call Scoring filter for Exec reports~ DescriptionNow that we have the call scoring feature, customers are asking for their report to be done only on calls with certain scores (either high or low).• include the call scoring filter in the Exec Reports section in Kiosk• use the same dropdown structure that we have in OD• put it before Recipients• ensure the scores filter applied for the report when fetching the calls for aanalisysSubtasksAdd subtaskLinked work itemsAdd linked work itemActivityComments History Work logAdd a comment….Who is working on this...? Status update...Thanks…..Pro tip: press (M) to commentC< 40 0 1 Support Daily- in 27m A 100% C/3 & • Mon 18 May 14:33:05ASK ROVO A® Lộ 0+ CreateBacklog~ * Improve StoryDetailsAssigneeReporterDevelopmentComponentsSub-ProductLabelsStory point estimateStory PointsOrganisationsPriorityFix versionsSprintDaysNeed QA& UnassignedAssign to meGalya Dimitrova[ Open with VS Code33 Create branch/ Create commitPlatformAdd optionsNoneNoneNoneNone= MediumNonePlatform Sprint 5 Q2NoneParentCanny LinksAdd optionJY-19309 Automated Al ScoringOpen Canny Links> More fields Original estimate, Time tracking> Automation 4 Rule executions> featureOS Open featureOs...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53071
|
1858
|
21
|
2026-05-18T11:33:05.848967+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103985848_m1.jpg...
|
Firefox
|
[JY-20878] SCIM > Allow customers to manage use [JY-20878] SCIM > Allow customers to manage user roles through SCIM - Jira — Work...
|
1
|
jiminny.atlassian.net/browse/JY-20878
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)11DOCKER (docker-compose){"type": "log""@timestamp": "2026-05-18T11:32:54Z"asticsearch", "data"],:"No livingconnections"},"tags" : ["warning"I {"type" : "log", "@timestamp" : "2026-05-18T11:32:547","tags" : ["error", "taskManager""taskManager"], "pid" :7, "message":"Failed to pollfor work: Error: NoLiving connections"}{"type" : "log":"2026-05-18T11:32:55Z", "tags" : ['"error""pid":7, "message" :"[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}{"type" : "log""@timestamp":"2026-05-18T11:32:57Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message" : "Unablerevive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:32:57Z", "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message" : "No living1 {"type" : "log"connections"}"@timestamp" : "2026-05-18T11:32:57Z", "tags" : ["error"ins",,"plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLivingconnections"}kibana{"type": "log"ticsearch","@timestamp": "2026-05-18T11:32:58Z", "tags" : ["error", "elas"data"], "pid" :7,"message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200*}1 {"type": "log","@timestamp" : "2026-05-18T11:33:00Z", "tags" : ["warning"asticsearch", "data"],"pid":7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:00Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}kibanains"1 {"type": "log"!,"@timestamp": "2026-05-18T11:33:00Z""tags" : ["error","plug, "taskManager""taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log".,"@timestamp" : "2026-05-18T11:33:00Z","tags" : ["error", "elasticsearch",, "data"], "pid":7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch: 9200"}kibanaI {"type" : "log", "@timestamp" : "2026-05-18T11:33:03Z","tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}1 {"type" : "log""@timestamp": "2026-05-18T11:33 :03Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:03Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7,"message": "No living connections"}1 {"type": "log", "@timestamp" : "2026-05-18T11:33:03Z", "tags" : ["error", "plug, "taskManager",, "taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View ConfigEnable Watch•HomeDMsActivityFilesLaterMore→Jiminny ...MetromrenCE# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario GeorgievNikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 27 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:33:056 0O Files+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
-5158242330745017121
|
NULL
|
click
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)11DOCKER (docker-compose){"type": "log""@timestamp": "2026-05-18T11:32:54Z"asticsearch", "data"],:"No livingconnections"},"tags" : ["warning"I {"type" : "log", "@timestamp" : "2026-05-18T11:32:547","tags" : ["error", "taskManager""taskManager"], "pid" :7, "message":"Failed to pollfor work: Error: NoLiving connections"}{"type" : "log":"2026-05-18T11:32:55Z", "tags" : ['"error""pid":7, "message" :"[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}{"type" : "log""@timestamp":"2026-05-18T11:32:57Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message" : "Unablerevive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:32:57Z", "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message" : "No living1 {"type" : "log"connections"}"@timestamp" : "2026-05-18T11:32:57Z", "tags" : ["error"ins",,"plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLivingconnections"}kibana{"type": "log"ticsearch","@timestamp": "2026-05-18T11:32:58Z", "tags" : ["error", "elas"data"], "pid" :7,"message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200*}1 {"type": "log","@timestamp" : "2026-05-18T11:33:00Z", "tags" : ["warning"asticsearch", "data"],"pid":7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:00Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}kibanains"1 {"type": "log"!,"@timestamp": "2026-05-18T11:33:00Z""tags" : ["error","plug, "taskManager""taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log".,"@timestamp" : "2026-05-18T11:33:00Z","tags" : ["error", "elasticsearch",, "data"], "pid":7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch: 9200"}kibanaI {"type" : "log", "@timestamp" : "2026-05-18T11:33:03Z","tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}1 {"type" : "log""@timestamp": "2026-05-18T11:33 :03Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:03Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7,"message": "No living connections"}1 {"type": "log", "@timestamp" : "2026-05-18T11:33:03Z", "tags" : ["error", "plug, "taskManager",, "taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View ConfigEnable Watch•HomeDMsActivityFilesLaterMore→Jiminny ...MetromrenCE# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario GeorgievNikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 27 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:33:056 0O Files+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53074
|
1858
|
22
|
2026-05-18T11:33:08.063135+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103988063_m1.jpg...
|
Firefox
|
[JY-20878] SCIM > Allow customers to manage use [JY-20878] SCIM > Allow customers to manage user roles through SCIM - Jira — Work...
|
1
|
jiminny.atlassian.net/browse/JY-20878
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82{"type": "log""@timestamp": "2026-05-18T11:32:57Z"asticsearch", "data"],:"No livingconnections"}"tags" : ["warning", "elI {"type" : "log", "@timestamp" : "2026-05-18T11:32:57Z""tags" : ["error", "taskManager""taskManager"], "pid" :7, "message":"Failed to pollfor work: Error: NoLiving connections"}{"type" : "log","@timestamp":"2026-05-18T11:32:58Z", "tags" : ['"error""pid":7, "message" :"[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}{"type" : "log""@timestamp": "2026-05-18T11:33:00Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message" : "Unable torevive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:00Z", "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message" : "No living1 {"type" : "log"connections"}"@timestamp" : "2026-05-18T11:33:00Z", "tags" : ["error","taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: Noconnections"}ticsearch"1 {"type": "log","@timestamp" : "2026-05-18T11:33 :00Z","tags" : ["error", "elas"data"], "pid" :7,"message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200*}ticsearch"1 {"type": "log"!,"@timestamp": "2026-05-18T11:33:03Z", "tags" : ["error","data"], "pid" :7, "message" :"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200*}1 {"type": "log", "@timestamp" : "2026-05-18T11:33:03Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:03Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:33:03Z", "tags" : ["error","taskManager", "taskManager"], "pid":7, "message":"Failed to poll for work: Error: NoLiving connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:33:05Z""tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}1 {"type" : "log""@timestamp": "2026-05-18T11:33 : 06Z""tags": ["warning"asticsearch", "data"], "pid" :7,"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33 :06Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7,"message": "No living connections"}1 {"type" : "log".,"@timestamp" : "2026-05-18T11:33:06Z", "tags" : ["error", "plug, "taskManager", "taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View ConfigHomeDMsActivityFilesLaterMore→Jiminny ...MetromrenCE# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario Georgiev. Nikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 27 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:33:076 0O Files+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
7485060249988645960
|
NULL
|
click
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82{"type": "log""@timestamp": "2026-05-18T11:32:57Z"asticsearch", "data"],:"No livingconnections"}"tags" : ["warning", "elI {"type" : "log", "@timestamp" : "2026-05-18T11:32:57Z""tags" : ["error", "taskManager""taskManager"], "pid" :7, "message":"Failed to pollfor work: Error: NoLiving connections"}{"type" : "log","@timestamp":"2026-05-18T11:32:58Z", "tags" : ['"error""pid":7, "message" :"[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}{"type" : "log""@timestamp": "2026-05-18T11:33:00Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message" : "Unable torevive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:00Z", "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message" : "No living1 {"type" : "log"connections"}"@timestamp" : "2026-05-18T11:33:00Z", "tags" : ["error","taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: Noconnections"}ticsearch"1 {"type": "log","@timestamp" : "2026-05-18T11:33 :00Z","tags" : ["error", "elas"data"], "pid" :7,"message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200*}ticsearch"1 {"type": "log"!,"@timestamp": "2026-05-18T11:33:03Z", "tags" : ["error","data"], "pid" :7, "message" :"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200*}1 {"type": "log", "@timestamp" : "2026-05-18T11:33:03Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:03Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:33:03Z", "tags" : ["error","taskManager", "taskManager"], "pid":7, "message":"Failed to poll for work: Error: NoLiving connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:33:05Z""tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}1 {"type" : "log""@timestamp": "2026-05-18T11:33 : 06Z""tags": ["warning"asticsearch", "data"], "pid" :7,"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33 :06Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7,"message": "No living connections"}1 {"type" : "log".,"@timestamp" : "2026-05-18T11:33:06Z", "tags" : ["error", "plug, "taskManager", "taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View ConfigHomeDMsActivityFilesLaterMore→Jiminny ...MetromrenCE# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario Georgiev. Nikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 27 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:33:076 0O Files+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
53071
|
NULL
|
NULL
|
NULL
|
|
53073
|
1859
|
32
|
2026-05-18T11:33:08.802841+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103988802_m2.jpg...
|
Firefox
|
[JY-20878] SCIM > Allow customers to manage use [JY-20878] SCIM > Allow customers to manage user roles through SCIM - Jira — Work...
|
1
|
jiminny.atlassian.net/browse/JY-20878
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Service-Desk - Queues - Platform team - Service space - Jira
Service-Desk - Queues - Platform team - Service space - Jira
Usage | Windsurf
Usage | Windsurf...
|
[{"role":"AXRadioButton","text [{"role":"AXRadioButton","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":4,"bounds":{"left":0.0,"top":0.0518755,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.06304868,"width":0.10106383,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Service-Desk - Queues - Platform team - Service space - Jira","depth":4,"bounds":{"left":0.0,"top":0.08459697,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Service-Desk - Queues - Platform team - Service space - Jira","depth":5,"bounds":{"left":0.013297873,"top":0.09577015,"width":0.10721409,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"},{"role":"AXRadioButton","text":"Usage | Windsurf","depth":4,"bounds":{"left":0.0,"top":0.11731844,"width":0.07962101,"height":0.032721467},"on_screen":true,"help_text":"","role_description":"tab","subrole":"AXTabButton","is_enabled":true,"is_focused":false,"is_selected":false},{"role":"AXStaticText","text":"Usage | Windsurf","depth":5,"bounds":{"left":0.013297873,"top":0.12849163,"width":0.029920213,"height":0.010774142},"on_screen":true,"help_text":"","role_description":"text","subrole":"AXUnknown"}]...
|
9049102461068892772
|
7225579252719825045
|
visual_change
|
accessibility
|
NULL
|
Platform Sprint 4 Q2 - Platform Team - Scrum Board Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Platform Sprint 4 Q2 - Platform Team - Scrum Board - Jira
Service-Desk - Queues - Platform team - Service space - Jira
Service-Desk - Queues - Platform team - Service space - Jira
Usage | Windsurf
Usage | Windsurf...
|
53072
|
NULL
|
NULL
|
NULL
|
|
53075
|
1859
|
33
|
2026-05-18T11:33:12.066794+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103992066_m2.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7673782238848625796
|
-8646559087753982588
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
PhpStormView Project: faVsco.js, menu
master, menu
PhpStormViewINavigareCodeLaravelRefactorWindowFV faVsco.js°9 master kProiect© ActivityController.phpC BaseService.php© SoftPhoneManager.php X>@ ConferenceHandlerv Conterencemanage,© TextMessagingService.php© SoftPhoneManager.ong• ConferenceCallbackHandler.phpConferenceManager.php>ODTOC Event> 0 Exceptione Jobfinal readonly class SoftPhoneManager implements ConferenceManagerA9V1AVprivate function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\A55format: 'Sorry, dialing %s is not available with your subscription.'• Please contact your manager to enable additional destinations.',"Values: Sregion ??' this number',>W ResolvelW Service>DVO$lead = $account = $opportunity = $contact = $stage = null;@ TwilioConstants.ohn© TwilioRepository.phpUoloaden7 UriGeneratortry Somnfesotven = ap( abstret: Crmomenfesot ven: oloss, 1'team' => SroomOwner->getTeam(),D UtilityM UuidIM Waveformn Wehhooks'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),Somservice + SereResotVver-spreparecmsenvzee 0):h Workflow> M Confiauration(82e0d, Beriount, Sopporetunaty, Scontact, Sstage) e Sornßervite-natenByPhoneD ConsoleD Commands>@ ActivitiesD) Analytics)mCnlondareSroomQwnen->aetIddvm Crm>@ Hubspot329} catch (\Throwable $throwable) {sthis->logger-serror C_METHOD__': Crm exception',l"pycention' => Sthoowahl.p› IntegrationApp1);Local ChangesLog xChanaes 5 filesE env.local appJiminnyDebugCommand.php app/Console/Commands< → ® Side-by-side viewer -@cb4ebf0c config/logging.phpDo not ianore y'driver' => 'errorlog'SoftPhoneManager.php app/Component/Twilio/Conference/ConferenceMar(C) TextMessacinaService.oho aoo/Services/Telephonv~ Unversioned Files 9 files= env.nikillocal anniE.env.other app© CanAccessAiReportsTest.php tests/Unit/Policies@ CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Ro) favicon.ico nublidE ids.txt appTraw_sqL_query.sql [EMAIL] app/Console/Commands/Crm/HubspotM+ WEBHOOK_FILTERING_IMPLEMENTATION.md appLevel → env "Lub Level', "Into)'path' => storage_path('logs/laravel.log').=custom.log= laravel.log« SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X556565568570573-575- 576=577578579Tx: AutovTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE 1D:select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_ at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;select x Tron aculvity searches where 10 = 10752select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55results where id IN (81):select * trom users where 1d IN 00055,15787,11985011select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43) = uuid;select * from teams:suppont Dally • In 2/m100% S2• Mon 18 May 14:33:11U AskJiminnyReportActivityServiceTest v& console [PROD]& console [EU]CascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token Falli+0 ..Go jiminny_jupiterI see the issue - the try-catch structure is broken. The catch block on line 147 for SocialAccountTokenInvalidExceptionmatching try block because the eRM usage code uines 115-146 Is not wrapped in a try block.BIS BIS XUAY• TextMessagingService.php7/7 tasks doneFixed the try-catch block structure in Textmessagingservice. The issue was that the catch block tor Socia AccountTokeninvalidException (line 147) had no matching try block - the CRM usage code wasn't wrapped in a try block after I added theFinal Implementation summary:1. SoftPhoneManager.php - Added CrmOwnerResolver fallback for both outbound and inbound Twilio calls2. TextMessagingService.php - Added CrmOwnerResolver fallback for both buildActivity and convertCustomerToCoulnerycode metnoas, with proper try-catch structureBoth imolementations now:• First try CrmOwnerResolver to find an active Salesforce token from the organization•If that fails, fall back to the original direct user assignment pattern• Let existing outer catch blocks handle any Socia AccountTokenInvalidException by settina CRM data to nullThis ensures Twilio calls will use another active Salesforce token when the admin token expires, preventing "Unknowncustomer" issues.Ask anvthina (%4L)+ @code SWE-1.61ditterenceCurrent vercion'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info').'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.l0g')'level' => env('LOG LEVEL', 'info').W Windsurf Teams 328:49 UTF-8 P 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53076
|
1858
|
23
|
2026-05-18T11:33:12.623497+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103992623_m1.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82X t1DOCKER (docker-compose)"taskManager""taskManager"], "pid" :7, "message" : "Failed to poll for work: Error: Noconnections"}1 {"type" : "log", "@timestamp": "2026-05-18T11:33:00Z", "tags" : ["error"ticsearch","data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}{"type" : "log","@timestamp" : "2026-05-18T11:33:03Z", "tags" : ["error""pid":7, "message":"[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}{"type" : "log""@timestamp": "2026-05-18T11:33:03Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message" : "Unable torevive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:03Z", "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message" : "No living1 {"type" : "log"connections"}"@timestamp" : "2026-05-18T11:33:03Z", "tags" : ["error"ins",,"plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLivingconnections"}kibana{"type": "log", "@timestamp" : "2026-05-18T11:33:05Z"ticsearch","tags" : ["error", "elas"data"], "pid" :7,"message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}1 {"type": "log","@timestamp" : "2026-05-18T11:33:06Z", "tags" : ["warning"asticsearch", "data"], "pid":7, "message" : "Unableto revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:06Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}kibanains"1 {"type": "log"!"@timestamp": "2026-05-18T11:33:06Z""tags" : ["error","plug, "taskManager","taskManager"], "pid" :7, "message" : "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log""@timestamp": "2026-05-18T11:33:08Z", "tags" : ["error", "elasticsearch", "data"], "pid":7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch: 9200"}kibana{"type" : "log""@timestamp": "2026-05-18T11:33:09Z""tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:33:09Z", "tags" : ["warning", "elasticsearch", "data"],"pid":7,"message": "No livingconnections"}kibanains"I {"type" : "log", "@timestamp" : "2026-05-18T11:33:09Z""tags" : ["error","plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:33:10Z", "tags" : ["error", "elasticsearch", "data"],"pid" :7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}View in Docker Desktop• View ConfigEnable Watch•8• Mon 18 May 14:33:126 0Jiminny ...Metrotrtencee# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario Georgiev. Nikolay IvanovLo James Graham *8 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 27 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:O Files+HomeDMsActivityFilesLaterMoreSign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
-1866535871199698730
|
NULL
|
click
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82X t1DOCKER (docker-compose)"taskManager""taskManager"], "pid" :7, "message" : "Failed to poll for work: Error: Noconnections"}1 {"type" : "log", "@timestamp": "2026-05-18T11:33:00Z", "tags" : ["error"ticsearch","data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}{"type" : "log","@timestamp" : "2026-05-18T11:33:03Z", "tags" : ["error""pid":7, "message":"[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}{"type" : "log""@timestamp": "2026-05-18T11:33:03Z","tags" : ["warning"asticsearch", "data"], "pid" :7, "message" : "Unable torevive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:03Z", "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message" : "No living1 {"type" : "log"connections"}"@timestamp" : "2026-05-18T11:33:03Z", "tags" : ["error"ins",,"plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLivingconnections"}kibana{"type": "log", "@timestamp" : "2026-05-18T11:33:05Z"ticsearch","tags" : ["error", "elas"data"], "pid" :7,"message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}1 {"type": "log","@timestamp" : "2026-05-18T11:33:06Z", "tags" : ["warning"asticsearch", "data"], "pid":7, "message" : "Unableto revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:06Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}kibanains"1 {"type": "log"!"@timestamp": "2026-05-18T11:33:06Z""tags" : ["error","plug, "taskManager","taskManager"], "pid" :7, "message" : "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log""@timestamp": "2026-05-18T11:33:08Z", "tags" : ["error", "elasticsearch", "data"], "pid":7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch: 9200"}kibana{"type" : "log""@timestamp": "2026-05-18T11:33:09Z""tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:33:09Z", "tags" : ["warning", "elasticsearch", "data"],"pid":7,"message": "No livingconnections"}kibanains"I {"type" : "log", "@timestamp" : "2026-05-18T11:33:09Z""tags" : ["error","plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:33:10Z", "tags" : ["error", "elasticsearch", "data"],"pid" :7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}View in Docker Desktop• View ConfigEnable Watch•8• Mon 18 May 14:33:126 0Jiminny ...Metrotrtencee# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario Georgiev. Nikolay IvanovLo James Graham *8 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 27 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:O Files+HomeDMsActivityFilesLaterMoreSign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53077
|
1859
|
34
|
2026-05-18T11:33:14.752444+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103994752_m2.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections...
|
[{"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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7429716278976468786
|
-8636355650190325311
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
PhpStormProiect vViewINavigareCodeLaravelRefactorWindowFV faVsco.js°9 master k© ActivityController.phpC BaseService.php© SoftPhoneManager.php X=custom.log= laravel.log« SF jiminny@localhost]4 HS_local [jiminny@localhost]& console [PROD]& console [EU]Cascade>@ ConferenceHandlerv M Conterencemanage,© SoftPhoneManager.ong© ConferenceCallbackHandler.phpConferenceManager.php>ODTOC Event> 0 ExceptionD Job>W ResolvelW Service,DVO@ TwilioConstants.ohn© TwilioRepository.phpUoloaden7 UriGeneratorD UtilityM UuidN Waveformn Wehhooksh Workflow> M ConfiaurationD ConsoleD Commands>@ ActivitiesD) Analytics)mCnlondarevm Crm>C Hubspot› IntegrationApp© TextMessagingService.phpA console [STAGING] XImplement Trial Ownefinal readonly class SoftPhoneManager implements ConferenceManagerA9.1AYprivate function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\A553Tx: AutovGo jiminny_jupiterTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE 1D:BIS BIS XUAYformat: 'Sorry, dialing %s is not available with your subscription.'• Please contact your manager to enable additional destinations.',"Values: Sregion ??' this number',556select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires at IS NULL);$lead = $account = $opportunity = $contact = $stage = null;select * from automated_report_results where report id IN (18, 33):try SormResotver = ppC abstact: CraovnenResotven :olas,'team' => SroomOwner->getTeam(),565select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),Somservice + SereResotVver-spreparecmsenvzee 0):568570(82e0d, Beriount, Sopporetunaty, Scontact, Sstage) e Sornßervite-natenByPhoneselect x Tron aculvity searches Where 10 = 10752select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55results where id IN (81):SroomQwnen->aetIdd329} catch (\Throwable $throwable) {sthis->logger-serror C_METHOD__': Crm exception',l"pycention' => Sthoowahl.p1);573-575- 576=577578579select * trom users where 1d IN 00055,15787,11 985071Fixina Redis Rate Limit Erronselect * trom users where qroup 10 IN (5/00:SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;Ask anvthina (%4L)+ @code SWE-1.6select * from teams:Local ChangesLog xChanaes 5 filesE env.local appJiminnyDebugCommand.php app/Console/Commandsphploaqina.ono confidSoftPhoneManager.php app/Component/Twilio/Conference/ConferenceMar(C) TextMessacinaService.oho aoo/Services/Telephonv~ Unversioned Files 9 files= env.nikillocal anniE .env.other app© CanAccessAiReportsTest.php tests/Unit/Policies@ CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Ro) favicon.ico nublidE ids.txt appTraw_sqL_query.sql [EMAIL] app/Console/Commands/Crm/HubspotM+ WEBHOOK_FILTERING_IMPLEMENTATION.md app< → ® Side-by-side viewer -Do not ianore y@cb4ebf0c config/logging.phpCurrent vercion'driver' => 'errorlog'Level → env "Lub Level', "Into)'path' => storage_path('logs/laravel.1og'),'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info').'path' => storage_path('logs/Laravel.log')'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info')."suppont Dally • In 21m100% S2• Mon 18 May 14:33:14AskJiminnyReportActivityServiceTest vFixing Redis Rate LimSalestorce Token FalliCascade+0 ..Wcascade Code x• ..Kick off a new proiect. Make changes1ditterenceМAMAYW Windsurf Teams 328:49 UTF-8 P 4 spaces ®...
|
53075
|
NULL
|
NULL
|
NULL
|
|
53079
|
1858
|
24
|
2026-05-18T11:33:17.822348+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103997822_m1.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","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}]...
|
-4235983745889776938
|
-8204421443435123770
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴2DOCKER (docker-compose)asticsearch", "data"],"message": "No livingconnections"}1 {"type" : "log","@timestamp" : "2026-05-18T11:33:09Z", "tags": ["error","plug, "taskManager", "taskManager"],"pid":7, "message": "Failed to poll.for work: Error: NoLiving connections"}ticsearch"{"type": "log","data"],,"@timestamp":'2026-05-18T11:33:10Z"tags": ["error""pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}{"type" : "log""@timestamp": "2026-05-18T11:33:13Z", "tags" : ["warning"asticsearch","data"], "pid" :7, "message" : "Unablerevive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:13Z" , "tags" : ["warning", "elasticsearch", "data"],'"pid":7, "message": "No livingconnections"}kibana1 {"type": "log".,"@timestamp" : "2026-05-18T11:33:13Z", "tags" : ["error", "plugins""taskManager", "taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}klbana{"type" : "log", "@timestamp" : "2026-05-18T11:33:13Z" , "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:13Z" , "tags" : ["warning", "elasticsearch", "data"], "pid":7, "message" : "No livingconnections"}kibanains"1 {"type" : "log","@timestamp": "2026-05-18T11:33:13Z""tags" : ["error","reporting", "esqueue","queue-worker"46mdeo -job querying failed: Error: No Living connectionsin"error"], "pid" :7, "message" : "mpau4y7h00070bdf86at sendReqWithConnection(/usr/share/kibana/node_modules/elasticsearch/src/lib/transport.js:266:15)\n(/usr/share/kibana/node_modules/elasticsearch/src/lib/connection_pool.js:243:7)\nprocess._tickCallback (internal/process/next_tick.js:61:11)"}kibana1 {"type" : "log"ticsearch","@timestamp" : "2026-05-18T11:33:13Z", "tags" : ["error","data"],"pid":7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200*}docker_lamp_1kibana{"type" : "log", "@timestamp" : "2026-05-18T11:33:15Z""tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:15Z" , "tags" : ["warning", "elasticsearch", "data"], "pid":7, "message": "No livingconnections"}kibana1 {"type" : "log"!ins","@timestamp": "2026-05-18T11:33:15Z","tags" : ["error","plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:33:15Z", "tags" : ["error", "elasticsearch", "data"],"pid" :7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}View in Docker Desktop• View ConfigEnable Watch•HomeDMsActivityFilesLaterMoreJiminny ...MetromrtencE# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...• Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka StoyanovaStoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov "8. Mario Georgiev. Nikolay IvanovLo James Graham *8 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiE. Lukas Kovalik y...AppsJira CloudToastSupport Daily • in 27 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas@ Files• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:33:176 0+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
53076
|
NULL
|
NULL
|
NULL
|
|
53078
|
1859
|
35
|
2026-05-18T11:33:17.862272+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103997862_m2.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\Twilio\Conference\ConferenceManager;
use Illuminate\Database\Eloquent;
use Illuminate\Support\Str;
use Jiminny\Component\ElasticSearch\Model\Observer;
use Jiminny\Component\Twilio\Conference\ConferenceManager;
use Jiminny\Component\Twilio\Exception\OutOfBoundsException;
use Jiminny\Component\Twilio\Exception\UnexpectedValueException;
use Jiminny\Component\Twilio\Resolver;
use Jiminny\Component\Twilio\VO;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Events;
use Jiminny\Exceptions\LogicException;
use Jiminny\Models;
use Jiminny\Models\Participant\Connection;
use Jiminny\Services\ActivityService;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Crm\CachedCrmServiceDecorator;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Services\Telephony\TwilioClientBuilder;
use Psr\Log\LoggerInterface;
use Twilio\Exceptions\ConfigurationException;
use Twilio\Exceptions\RestException;
use Twilio\Exceptions\TwilioException;
use Twilio\TwiML;
final readonly class SoftPhoneManager implements ConferenceManager
{
public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;
public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';
public function __construct(
private ProviderRegistry $crmProviderRegistry,
private Webhook $urlGenerator,
private ActivityService $activityService,
private LoggerInterface $logger,
private TwilioClientBuilder $twilioClientBuilder,
) {
}
public function create(VO\ConferenceManager\ActivityCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'data' => $data,
'instanceof' => get_class($data),
]);
$activity = Observer::skipEvents(function () use ($data) {
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundCreate) {
$activity = $this->createInboundActivity($data);
} elseif ($data instanceof VO\ConferenceManager\SoftPhoneOutboundCreate) {
$activity = $this->createOutboundActivity($data);
} else {
throw new LogicException(sprintf(
'Unknown softphone implementation: %s',
get_class($data)
));
}
return $activity;
});
$this->logger->debug(__METHOD__ . ': done', [
'instanceof' => get_class($data),
'activity' => $activity->id_string,
]);
return $activity;
}
private function createOutboundActivity(VO\ConferenceManager\SoftPhoneOutboundCreate $data): Models\Activity
{
$startTime = microtime(true);
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
$now = now();
$user = $data->getUser();
$team = $user->getTeam();
$device = $data->getDevice();
$phoneNumber = $data->getPhoneNumber();
$route = $data->getPhoneRoute();
if (! $this->activityService->teamCanDial($team, $phoneNumber)) {
$region = getRegionByNumber($phoneNumber, $user->getCountryCode());
throw new LogicException(sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? 'this number'
));
}
$lead = null;
$account = null;
$contact = null;
$stage = null;
$opportunity = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $user,
'providerSlug' => $team->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
} catch (\Throwable $throwable) {
$this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [
'exception' => $throwable,
'user_id' => $user->getId(),
'team_id' => $team->getId(),
]);
$crmService = $this->crmProviderRegistry->get($team->crm->provider);
if (! $user->isCrmRequired()) {
$crmService->setUser($user->team->owner);
} else {
$crmService->setUser($user);
}
}
$prospectId = $data->getProspectId();
try {
if ($prospectId !== null) {
$crmStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [
'prospect_id' => $prospectId,
'object_type' => $data->getObjectType(),
]);
[$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(
$prospectId,
$data->getObjectType()
);
$this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),
'found_lead' => $lead !== null,
'found_account' => $account !== null,
'found_opportunity' => $opportunity !== null,
'found_contact' => $contact !== null,
]);
// Try to attach a contact if we have an opportunity.
if ($opportunity !== null && $contact === null) {
$matchStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');
[, , , $contact, ] = $crmService->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
$this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),
'found_contact' => $contact !== null,
]);
}
} else {
$decorator = resolve(CachedCrmServiceDecorator::class);
$decorator->setCrmService($crmService);
[$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
}
} catch (\Throwable $throwable) {
$this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.
}
if ($data->hasProspectStage()) {
/** @var Models\Stage $stage */
$stage = $team->crm->stages()->uuid($data->getProspectStage());
}
$dbStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting activity creation in DB');
$activity = $user->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE,
'provider' => Models\Activity::PROVIDER_TWILIO,
'device_id' => $device->id ?? null,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'value' => $opportunity ? $opportunity->value : null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_PENDING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),
'scheduled_start_time' => $now,
]);
$this->logger->info('[SoftPhoneManager] Activity created in DB', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),
'activity_id' => $activity->id ?? null,
]);
if (! $activity instanceof Models\Activity) {
throw new LogicException('Activity model expected');
}
$regionId = null;
if ($user->hasRegionId()) {
$regionId = $user->getRegionId();
} elseif ($data->hasRegion()) {
$regionId = $data->getRegion()->getId();
}
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $regionId,
'is_muted' => false,
]);
$phoneRouteResolver = new Resolver\PhoneRouteResolver($user, $route);
$phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();
$connectionType = $phoneRouteResolver->getConnectionType();
$callerId = $this->determineUserCallerId($user);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'user_id' => $user->id,
'email' => $user->email,
'name' => $user->name,
'phone_number' => $phoneRouteResolver->mustUsePSTN()
? $phoneNumberOrigin
: $callerId,
]);
$caller->connections()->create([
'session_id' => $session->getId(),
'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',
'phone' => $callerId,
'type' => $connectionType,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $phoneNumber,
'name' => $activity->prospect_name,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'phone' => $phoneNumber,
'direction' => 'outbound-api',
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$activity->refresh();
$this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [
'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),
'activity_id' => $activity->id_string,
]);
$this->logger->debug(__METHOD__ . ': done', [
'data' => $data,
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
return $activity;
}
private function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$roomOwner = $data->getUser();
$team = $roomOwner->getTeam();
$phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();
$callerId = $data->getCallerId();
$callerCallSid = $data->getCallSid();
$now = now();
if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {
$region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());
throw new LogicException(
sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? ' this number',
),
);
}
$lead = $account = $opportunity = $contact = $stage = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $roomOwner->getTeam(),
'integrationAdmin' => $roomOwner,
'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
[$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(
$callerId,
null,
$roomOwner->getId()
);
} catch (\Throwable $throwable) {
$this->logger->error(__METHOD__ . ': crm exception', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening
}
/** @var Models\Activity $activity */
$activity = $roomOwner->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE_INBOUND,
'provider' => Models\Activity::PROVIDER_TWILIO,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_RINGING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),
'scheduled_start_time' => $now,
]);
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $roomOwner->getRegionId(),
'is_muted' => false,
]);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $callerId,
'name' => $activity->prospect_name,
'enter_time' => $now,
]);
$caller->connections()->create([
'telephony_provider_id' => $callerCallSid,
'session_id' => $session->getId(),
'phone' => $callerId,
'direction' => 'inbound',
'type' => Connection::TYPE_PSTN,
'opened_at' => $now,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'user_id' => $roomOwner->id,
'email' => $roomOwner->email,
'name' => $roomOwner->name,
'phone_number' => $phoneNumber,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'direction' => 'outbound-api',
'phone' => $phoneNumber,
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$this->logger->debug(__METHOD__ . ': done', [
'callSid' => $data->getCallSid(),
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
$activity->refresh();
return $activity;
}
private function getVoiceRecordingReasonCode(Models\Team $team, string $phoneNumber): int
{
$this->logger->debug(__METHOD__, [
'team' => $team->id_string,
'phone' => $phoneNumber,
]);
if (
! $team->hasComplianceModeRecordingPrompt()
&& ! $team->hasComplianceModeRecordingRestrictToOneSide()
&& ! $team->hasComplianceModeRecordingRestrictRecording()
) {
return 0;
}
/** @var Models\VoiceConsentPrefix|null $voiceConsentPolicy */
$voiceConsentPolicy = Models\VoiceConsentPrefix::query()
->whereRaw("? LIKE CONCAT(destination_prefix, '%')", [$phoneNumber])
->where(static function (Eloquent\Builder $builder) use ($team): void {
$builder
->whereNull('team_id')
->orWhere('team_id', $team->getId());
})
->orderByDesc('team_id')
->first();
if (! $voiceConsentPolicy instanceof Models\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {
return 0;
}
if ($team->hasComplianceModeRecordingPrompt()) {
// This number requires consent from the other party before recording the call.
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;
}
if ($team->hasComplianceModeRecordingRestrictToOneSide()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;
}
if ($team->hasComplianceModeRecordingRestrictRecording()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;
}
return 0;
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOrganiser(Models\Activity $activity): void
{
$this->logger->debug(__METHOD__, [
'activity' => $activity->id_string,
]);
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$canBeDialed =
$activity->isTypeSoftPhone()
&& ! $activity->hasStarted()
&& $activity->isPending()
&& $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->exists();
if (! $canBeDialed) {
throw new OutOfBoundsException('Cannot make an outbound call to this activity.');
}
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->firstOrFail();
/** @var Models\Session $session */
$session = $organiserConnection->getSession();
$twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
]);
// What number will the organizer see as caller id
$callerId = $roomOwner->getSoftPhoneNumber();
if ($callerId === null) {
$callee = $activity->participants()->whereNull('user_id')->first();
// This is a bridge call. We make it look like the callee is calling the organizer
$callerId = $callee->phone_number;
}
// What number will the prospect see as caller id
$calleeId = $organiser->getPhoneNumber();
$this->logger->debug(__METHOD__ . ': twilio call', [
'calleeId' => $calleeId,
'callerId' => $callerId,
]);
$twilioParticipant = $twilio->createParticipantInstance(
$session->id_string,
$callerId,
$calleeId,
$twilioConferenceSettings
);
$organiserConnection
->setTelephonyProviderId($twilioParticipant->callSid)
->save();
$session
->setTelephonyProviderId($twilioParticipant->conferenceSid)
->save();
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'session' => $session->id_string,
]);
}
public function dialCoach(Models\Activity $activity, Models\User $user): void
{
throw new \LogicException('Not implemented yet');
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function terminate(VO\ConferenceManager\ActivityTerminate $data): void
{
if (! $data instanceof VO\ConferenceManager\SoftphoneTerminate) {
throw new UnexpectedValueException(sprintf(
'Expecting an instance of "%s", but "%s" given.',
VO\ConferenceManager\SoftphoneTerminate::class,
\get_class($data)
));
}
$this->logger->debug(__METHOD__, [
'activity' => $data->getActivity()->id_string,
'user' => $data->getUser()->id_string,
]);
$activity = $data->getActivity();
$roomOwner = $activity->getUser();
$isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();
if ($isCancelling) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
}
/** @var Models\Session $session */
$session = $activity->sessions->first();
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->whereIn('type', [
Connection::TYPE_WEBRTC,
Connection::TYPE_PSTN,
])
->firstOrFail();
if (! $organiserConnection->hasTelephonyProviderId()) {
throw new LogicException(
'Unable to terminate call as we do not yet know the organiser telephony provider id',
);
}
$twilioClient = $this->twilioClientBuilder->build($activity->getTeam());
$twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());
$session->hasTelephonyProviderId()
&& $twilioClient->terminateConference($session->getTelephonyProviderId());
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'session' => $session->id_string,
]);
}
public function answer(VO\ConferenceManager\ActivityAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundAnswer) {
return $this->answerInboundCall($data);
}
if ($data instanceof VO\ConferenceManager\SoftPhoneOutboundAnswer) {
return $this->answerOutboundCall($data);
}
throw new LogicException('Unable to answer - not supported activity answer instance');
}
private function answerInboundCall(VO\ConferenceManager\SoftPhoneInboundAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$dateTime = now();
$activity = $this->create(
new VO\ConferenceManager\SoftPhoneInboundCreate(
$data->getCallerId(),
$data->getCallSid(),
$data->getUser()
)
);
/** @var Models\Participant $activityGuest */
$activityGuest = $activity->from;
$activityGuest
->setEnterTime($dateTime)
->save();
/** @var Connection $connection */
$connection = $activityGuest->connections()
->where('type', Connection::TYPE_PSTN)
->firstOrFail();
$connection
->setTelephonyProviderId($data->getCallSid())
->setOpenedAt($dateTime)
->save();
event(new Events\Activities\Connections\Opened($connection));
return $this->joinTwilioConference($connection, [
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]);
}
private function answerOutboundCall(VO\ConferenceManager\SoftPhoneOutboundAnswer $data): TwiML\VoiceResponse
{
$connection = $data->getConnection();
$participant = $connection->getParticipant();
$activity = $data->getActivity();
$organiser = $activity->organizer;
$dateTime = now();
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
'connection' => $connection->id_string,
'participant' => $participant->id_string,
'activity' => $activity->id_string,
]);
if (! $participant->hasEnterTime()) {
$participant->update([
'enter_time' => $dateTime,
]);
}
$connection->update([
'is_muted' => $participant->isCoach(),
'opened_at' => $dateTime,
]);
event(new Events\Activities\Connections\Opened($connection));
if ($participant->isCoach()) {
/** @var Connection $roomOwnerConnection */
$roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();
return $this->joinTwilioConference(
$connection,
[
'coach' => $roomOwnerConnection->getTelephonyProviderId(),
]
);
}
return $this->joinTwilioConference(
$connection,
[
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]
);
}
private function determineUserCallerId(Models\User $user): string
{
if ($user->hasCallerId()) {
return $user->getCallerId();
}
// TODO: We should require a caller id to be verified before using the dialer
return $user->getSoftPhoneNumber() ?? $user->phone;
}
private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool
{
$startsWith = '[HTTP 404] Unable to create record: The requested resource';
$endsWith = 'was not found';
return Str::startsWith($exceptionMessage, $startsWith)
&& Str::endsWith($exceptionMessage, $endsWith);
}
/**
* @throws RestException
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOtherParty(Connection $callerConnection): self
{
$session = $callerConnection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$this->logger->debug(__METHOD__, [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {
throw new UnexpectedValueException('Unexpected activity type');
}
$callee = $activity->to;
$calleeId = $callee->getPhoneNumber();
/** @var Connection $calleeConnection */
$calleeConnection = $callee->connections()
->whereIn('type', [
Connection::TYPE_PSTN,
Connection::TYPE_WEBRTC,
])
->firstOrFail();
$caller = $activity->from;
// we need to take the number from the connection
// as the one we store on participant level might be the one we call to the rep via PSTN mode
$callerId = $callerConnection->getPhoneNumber();
if (! $caller->is($callerConnection->getParticipant())) {
throw new LogicException(sprintf(
'Unexpected caller participant. Expected: "%s", Actual: "%s"',
$caller->id_string,
$callerConnection->getParticipant()->id_string
));
}
$twilioConferenceSettings = [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
];
$hasInboundRecording = $activity->isTypeSoftphoneInbound()
&& $roomOwner->checkSoftphoneInboundRecordPreference();
$hasOutboundRecording = $activity->isTypeSoftPhone()
&& $roomOwner->checkSoftphoneOutboundRecordPreference();
$isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;
$isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()
|| $activity->hasRecordingReasonComplianceRestricted();
if ($isRecordingEnabled && ! $isRecordingRestricted) {
$twilioConferenceSettings = array_merge($twilioConferenceSettings, [
'record' => true,
'recordingChannels' => 'dual',
'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),
'recordingStatusCallbackMethod' => 'POST',
'recordingStatusCallbackEvent' => 'in-progress completed absent',
]);
if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {
$twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()
? 'outbound'
: 'inbound';
}
}
try {
if ($activity->hasRecordingReasonCompliancePrompted()) {
$this->logger->info('[Softphone] Request compliance prompt via outbound call', [
'activity_uuid' => $activity->getUuid(),
'session' => $session->id_string,
'conference_sid' => $session->getTelephonyProviderId(),
]);
$callInstance = $twilio->createCall(
$callerId,
$calleeId,
$this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [
'session' => $session->id_string,
'connection' => $calleeConnection->id_string,
]),
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $callInstance->sid,
'parent_connection_id' => $callerConnection->getId(),
]);
} else {
$twilioParticipantConnection = $twilio->createParticipantInstance(
$session->getTelephonyProviderId(),
$callerId,
$calleeId,
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $twilioParticipantConnection->callSid,
]);
}
} catch (RestException $restException) {
if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {
throw $restException;
}
if (! $activity->isInFiniteState()) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
$message = __METHOD__ . ': activity.status -> cancelled';
} else {
$message = sprintf(
__METHOD__ . ': not setting to cancelled, activity.status is %s',
$activity->getStatus()
);
}
$this->logger->info($message, [
'activity' => $activity->id_string,
'exception' => $restException,
]);
}
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
return $this;
}
private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array
{
$session = $connection->getSession();
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => false,
'record' => false,
'waitUrl' => '',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]),
'statusCallbackEvent' => [
'initiated',
'ringing',
'answered',
'completed',
],
'statusCallbackMethod' => 'POST',
'conferenceStatusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
]),
'conferenceStatusCallbackMethod' => 'POST',
'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function applyDefaultTwilioConferenceSettings(Models\Session $session, array $override = []): array
{
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => false,
'endConferenceOnExit' => false,
'waitUrl' => '',
'statusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
// 'speaker',
]),
'statusCallbackMethod' => 'POST',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function joinTwilioConference(Connection $connection, array $options = []): TwiML\VoiceResponse
{
$session = $connection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$this->logger->debug(__METHOD__, [
'connection' => $connection->id_string,
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
]);
$voiceResponse = new TwiML\VoiceResponse();
$voiceResponse->dial()
->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]))
->conference(
$session->getIdString(),
$this->applyDefaultTwilioConferenceSettings($session, $options)
);
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'result' => $voiceResponse,
]);
return $voiceResponse;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny_jupiter
Sync Changes
Hide This Notification
Code changed:
Hide
19
19
17
Previous Highlighted Error
Next 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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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":"9","depth":4,"bounds":{"left":0.3929521,"top":0.10055866,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.40292552,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.4119016,"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.4192154,"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\\Component\\Twilio\\Conference\\ConferenceManager;\n\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Support\\Str;\nuse Jiminny\\Component\\ElasticSearch\\Model\\Observer;\nuse Jiminny\\Component\\Twilio\\Conference\\ConferenceManager;\nuse Jiminny\\Component\\Twilio\\Exception\\OutOfBoundsException;\nuse Jiminny\\Component\\Twilio\\Exception\\UnexpectedValueException;\nuse Jiminny\\Component\\Twilio\\Resolver;\nuse Jiminny\\Component\\Twilio\\VO;\nuse Jiminny\\Component\\UrlGenerator\\Webhook;\nuse Jiminny\\Events;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Services\\ActivityService;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Crm\\CachedCrmServiceDecorator;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Services\\Telephony\\TwilioClientBuilder;\nuse Psr\\Log\\LoggerInterface;\nuse Twilio\\Exceptions\\ConfigurationException;\nuse Twilio\\Exceptions\\RestException;\nuse Twilio\\Exceptions\\TwilioException;\nuse Twilio\\TwiML;\n\nfinal readonly class SoftPhoneManager implements ConferenceManager\n{\n public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;\n public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';\n\n public function __construct(\n private ProviderRegistry $crmProviderRegistry,\n private Webhook $urlGenerator,\n private ActivityService $activityService,\n private LoggerInterface $logger,\n private TwilioClientBuilder $twilioClientBuilder,\n ) {\n }\n\n public function create(VO\\ConferenceManager\\ActivityCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n 'instanceof' => get_class($data),\n ]);\n\n $activity = Observer::skipEvents(function () use ($data) {\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundCreate) {\n $activity = $this->createInboundActivity($data);\n } elseif ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundCreate) {\n $activity = $this->createOutboundActivity($data);\n } else {\n throw new LogicException(sprintf(\n 'Unknown softphone implementation: %s',\n get_class($data)\n ));\n }\n\n return $activity;\n });\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'instanceof' => get_class($data),\n 'activity' => $activity->id_string,\n ]);\n\n return $activity;\n }\n\n private function createOutboundActivity(VO\\ConferenceManager\\SoftPhoneOutboundCreate $data): Models\\Activity\n {\n $startTime = microtime(true);\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n $now = now();\n $user = $data->getUser();\n $team = $user->getTeam();\n $device = $data->getDevice();\n $phoneNumber = $data->getPhoneNumber();\n $route = $data->getPhoneRoute();\n\n if (! $this->activityService->teamCanDial($team, $phoneNumber)) {\n $region = getRegionByNumber($phoneNumber, $user->getCountryCode());\n\n throw new LogicException(sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? 'this number'\n ));\n }\n\n $lead = null;\n $account = null;\n $contact = null;\n $stage = null;\n $opportunity = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $user,\n 'providerSlug' => $team->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n } catch (\\Throwable $throwable) {\n $this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [\n 'exception' => $throwable,\n 'user_id' => $user->getId(),\n 'team_id' => $team->getId(),\n ]);\n $crmService = $this->crmProviderRegistry->get($team->crm->provider);\n if (! $user->isCrmRequired()) {\n $crmService->setUser($user->team->owner);\n } else {\n $crmService->setUser($user);\n }\n }\n\n $prospectId = $data->getProspectId();\n\n try {\n if ($prospectId !== null) {\n $crmStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [\n 'prospect_id' => $prospectId,\n 'object_type' => $data->getObjectType(),\n ]);\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(\n $prospectId,\n $data->getObjectType()\n );\n $this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),\n 'found_lead' => $lead !== null,\n 'found_account' => $account !== null,\n 'found_opportunity' => $opportunity !== null,\n 'found_contact' => $contact !== null,\n ]);\n\n // Try to attach a contact if we have an opportunity.\n if ($opportunity !== null && $contact === null) {\n $matchStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');\n [, , , $contact, ] = $crmService->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n $this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),\n 'found_contact' => $contact !== null,\n ]);\n }\n } else {\n $decorator = resolve(CachedCrmServiceDecorator::class);\n $decorator->setCrmService($crmService);\n\n [$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n }\n } catch (\\Throwable $throwable) {\n $this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.\n }\n\n if ($data->hasProspectStage()) {\n /** @var Models\\Stage $stage */\n $stage = $team->crm->stages()->uuid($data->getProspectStage());\n }\n\n $dbStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting activity creation in DB');\n $activity = $user->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'device_id' => $device->id ?? null,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'value' => $opportunity ? $opportunity->value : null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_PENDING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),\n 'scheduled_start_time' => $now,\n ]);\n $this->logger->info('[SoftPhoneManager] Activity created in DB', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),\n 'activity_id' => $activity->id ?? null,\n ]);\n\n if (! $activity instanceof Models\\Activity) {\n throw new LogicException('Activity model expected');\n }\n\n $regionId = null;\n if ($user->hasRegionId()) {\n $regionId = $user->getRegionId();\n } elseif ($data->hasRegion()) {\n $regionId = $data->getRegion()->getId();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $regionId,\n 'is_muted' => false,\n ]);\n\n $phoneRouteResolver = new Resolver\\PhoneRouteResolver($user, $route);\n $phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();\n $connectionType = $phoneRouteResolver->getConnectionType();\n $callerId = $this->determineUserCallerId($user);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'user_id' => $user->id,\n 'email' => $user->email,\n 'name' => $user->name,\n 'phone_number' => $phoneRouteResolver->mustUsePSTN()\n ? $phoneNumberOrigin\n : $callerId,\n ]);\n\n $caller->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',\n 'phone' => $callerId,\n 'type' => $connectionType,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $phoneNumber,\n 'name' => $activity->prospect_name,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'phone' => $phoneNumber,\n 'direction' => 'outbound-api',\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $activity->refresh();\n\n $this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [\n 'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'activity_id' => $activity->id_string,\n ]);\n $this->logger->debug(__METHOD__ . ': done', [\n 'data' => $data,\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n return $activity;\n }\n\n private function createInboundActivity(VO\\ConferenceManager\\SoftPhoneInboundCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $roomOwner = $data->getUser();\n $team = $roomOwner->getTeam();\n $phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();\n $callerId = $data->getCallerId();\n $callerCallSid = $data->getCallSid();\n\n $now = now();\n\n if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {\n $region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());\n\n throw new LogicException(\n sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? ' this number',\n ),\n );\n }\n\n $lead = $account = $opportunity = $contact = $stage = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $roomOwner->getTeam(),\n 'integrationAdmin' => $roomOwner,\n 'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(\n $callerId,\n null,\n $roomOwner->getId()\n );\n } catch (\\Throwable $throwable) {\n $this->logger->error(__METHOD__ . ': crm exception', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening\n }\n\n /** @var Models\\Activity $activity */\n $activity = $roomOwner->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE_INBOUND,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_RINGING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),\n 'scheduled_start_time' => $now,\n ]);\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $roomOwner->getRegionId(),\n 'is_muted' => false,\n ]);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $callerId,\n 'name' => $activity->prospect_name,\n 'enter_time' => $now,\n ]);\n\n $caller->connections()->create([\n 'telephony_provider_id' => $callerCallSid,\n 'session_id' => $session->getId(),\n 'phone' => $callerId,\n 'direction' => 'inbound',\n 'type' => Connection::TYPE_PSTN,\n 'opened_at' => $now,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'user_id' => $roomOwner->id,\n 'email' => $roomOwner->email,\n 'name' => $roomOwner->name,\n 'phone_number' => $phoneNumber,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => 'outbound-api',\n 'phone' => $phoneNumber,\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'callSid' => $data->getCallSid(),\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n $activity->refresh();\n\n return $activity;\n }\n\n private function getVoiceRecordingReasonCode(Models\\Team $team, string $phoneNumber): int\n {\n $this->logger->debug(__METHOD__, [\n 'team' => $team->id_string,\n 'phone' => $phoneNumber,\n ]);\n\n if (\n ! $team->hasComplianceModeRecordingPrompt()\n && ! $team->hasComplianceModeRecordingRestrictToOneSide()\n && ! $team->hasComplianceModeRecordingRestrictRecording()\n ) {\n return 0;\n }\n\n /** @var Models\\VoiceConsentPrefix|null $voiceConsentPolicy */\n $voiceConsentPolicy = Models\\VoiceConsentPrefix::query()\n ->whereRaw(\"? LIKE CONCAT(destination_prefix, '%')\", [$phoneNumber])\n ->where(static function (Eloquent\\Builder $builder) use ($team): void {\n $builder\n ->whereNull('team_id')\n ->orWhere('team_id', $team->getId());\n })\n ->orderByDesc('team_id')\n ->first();\n\n if (! $voiceConsentPolicy instanceof Models\\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {\n return 0;\n }\n\n if ($team->hasComplianceModeRecordingPrompt()) {\n // This number requires consent from the other party before recording the call.\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;\n }\n\n if ($team->hasComplianceModeRecordingRestrictToOneSide()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;\n }\n\n if ($team->hasComplianceModeRecordingRestrictRecording()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;\n }\n\n return 0;\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOrganiser(Models\\Activity $activity): void\n {\n $this->logger->debug(__METHOD__, [\n 'activity' => $activity->id_string,\n ]);\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $canBeDialed =\n $activity->isTypeSoftPhone()\n && ! $activity->hasStarted()\n && $activity->isPending()\n && $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->exists();\n\n if (! $canBeDialed) {\n throw new OutOfBoundsException('Cannot make an outbound call to this activity.');\n }\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->firstOrFail();\n\n /** @var Models\\Session $session */\n $session = $organiserConnection->getSession();\n\n $twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ]);\n\n // What number will the organizer see as caller id\n $callerId = $roomOwner->getSoftPhoneNumber();\n\n if ($callerId === null) {\n $callee = $activity->participants()->whereNull('user_id')->first();\n // This is a bridge call. We make it look like the callee is calling the organizer\n $callerId = $callee->phone_number;\n }\n\n // What number will the prospect see as caller id\n $calleeId = $organiser->getPhoneNumber();\n\n $this->logger->debug(__METHOD__ . ': twilio call', [\n 'calleeId' => $calleeId,\n 'callerId' => $callerId,\n ]);\n\n $twilioParticipant = $twilio->createParticipantInstance(\n $session->id_string,\n $callerId,\n $calleeId,\n $twilioConferenceSettings\n );\n\n $organiserConnection\n ->setTelephonyProviderId($twilioParticipant->callSid)\n ->save();\n\n $session\n ->setTelephonyProviderId($twilioParticipant->conferenceSid)\n ->save();\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function dialCoach(Models\\Activity $activity, Models\\User $user): void\n {\n throw new \\LogicException('Not implemented yet');\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function terminate(VO\\ConferenceManager\\ActivityTerminate $data): void\n {\n if (! $data instanceof VO\\ConferenceManager\\SoftphoneTerminate) {\n throw new UnexpectedValueException(sprintf(\n 'Expecting an instance of \"%s\", but \"%s\" given.',\n VO\\ConferenceManager\\SoftphoneTerminate::class,\n \\get_class($data)\n ));\n }\n\n $this->logger->debug(__METHOD__, [\n 'activity' => $data->getActivity()->id_string,\n 'user' => $data->getUser()->id_string,\n ]);\n\n $activity = $data->getActivity();\n $roomOwner = $activity->getUser();\n\n $isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();\n\n if ($isCancelling) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions->first();\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->whereIn('type', [\n Connection::TYPE_WEBRTC,\n Connection::TYPE_PSTN,\n ])\n ->firstOrFail();\n\n if (! $organiserConnection->hasTelephonyProviderId()) {\n throw new LogicException(\n 'Unable to terminate call as we do not yet know the organiser telephony provider id',\n );\n }\n\n $twilioClient = $this->twilioClientBuilder->build($activity->getTeam());\n\n $twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());\n\n $session->hasTelephonyProviderId()\n && $twilioClient->terminateConference($session->getTelephonyProviderId());\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function answer(VO\\ConferenceManager\\ActivityAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundAnswer) {\n return $this->answerInboundCall($data);\n }\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundAnswer) {\n return $this->answerOutboundCall($data);\n }\n\n throw new LogicException('Unable to answer - not supported activity answer instance');\n }\n\n private function answerInboundCall(VO\\ConferenceManager\\SoftPhoneInboundAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $dateTime = now();\n\n $activity = $this->create(\n new VO\\ConferenceManager\\SoftPhoneInboundCreate(\n $data->getCallerId(),\n $data->getCallSid(),\n $data->getUser()\n )\n );\n\n /** @var Models\\Participant $activityGuest */\n $activityGuest = $activity->from;\n\n $activityGuest\n ->setEnterTime($dateTime)\n ->save();\n\n /** @var Connection $connection */\n $connection = $activityGuest->connections()\n ->where('type', Connection::TYPE_PSTN)\n ->firstOrFail();\n\n $connection\n ->setTelephonyProviderId($data->getCallSid())\n ->setOpenedAt($dateTime)\n ->save();\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n return $this->joinTwilioConference($connection, [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]);\n }\n\n private function answerOutboundCall(VO\\ConferenceManager\\SoftPhoneOutboundAnswer $data): TwiML\\VoiceResponse\n {\n $connection = $data->getConnection();\n $participant = $connection->getParticipant();\n $activity = $data->getActivity();\n $organiser = $activity->organizer;\n $dateTime = now();\n\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n 'connection' => $connection->id_string,\n 'participant' => $participant->id_string,\n 'activity' => $activity->id_string,\n ]);\n\n if (! $participant->hasEnterTime()) {\n $participant->update([\n 'enter_time' => $dateTime,\n ]);\n }\n\n $connection->update([\n 'is_muted' => $participant->isCoach(),\n 'opened_at' => $dateTime,\n ]);\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n if ($participant->isCoach()) {\n /** @var Connection $roomOwnerConnection */\n $roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'coach' => $roomOwnerConnection->getTelephonyProviderId(),\n ]\n );\n }\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]\n );\n }\n\n private function determineUserCallerId(Models\\User $user): string\n {\n if ($user->hasCallerId()) {\n return $user->getCallerId();\n }\n\n // TODO: We should require a caller id to be verified before using the dialer\n return $user->getSoftPhoneNumber() ?? $user->phone;\n }\n\n private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool\n {\n $startsWith = '[HTTP 404] Unable to create record: The requested resource';\n $endsWith = 'was not found';\n\n return Str::startsWith($exceptionMessage, $startsWith)\n && Str::endsWith($exceptionMessage, $endsWith);\n }\n\n /**\n * @throws RestException\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOtherParty(Connection $callerConnection): self\n {\n $session = $callerConnection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $this->logger->debug(__METHOD__, [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {\n throw new UnexpectedValueException('Unexpected activity type');\n }\n\n $callee = $activity->to;\n $calleeId = $callee->getPhoneNumber();\n\n /** @var Connection $calleeConnection */\n $calleeConnection = $callee->connections()\n ->whereIn('type', [\n Connection::TYPE_PSTN,\n Connection::TYPE_WEBRTC,\n ])\n ->firstOrFail();\n\n $caller = $activity->from;\n\n // we need to take the number from the connection\n // as the one we store on participant level might be the one we call to the rep via PSTN mode\n $callerId = $callerConnection->getPhoneNumber();\n\n if (! $caller->is($callerConnection->getParticipant())) {\n throw new LogicException(sprintf(\n 'Unexpected caller participant. Expected: \"%s\", Actual: \"%s\"',\n $caller->id_string,\n $callerConnection->getParticipant()->id_string\n ));\n }\n\n $twilioConferenceSettings = [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ];\n\n $hasInboundRecording = $activity->isTypeSoftphoneInbound()\n && $roomOwner->checkSoftphoneInboundRecordPreference();\n\n $hasOutboundRecording = $activity->isTypeSoftPhone()\n && $roomOwner->checkSoftphoneOutboundRecordPreference();\n\n $isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;\n\n $isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()\n || $activity->hasRecordingReasonComplianceRestricted();\n\n if ($isRecordingEnabled && ! $isRecordingRestricted) {\n $twilioConferenceSettings = array_merge($twilioConferenceSettings, [\n 'record' => true,\n 'recordingChannels' => 'dual',\n 'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),\n 'recordingStatusCallbackMethod' => 'POST',\n 'recordingStatusCallbackEvent' => 'in-progress completed absent',\n ]);\n\n if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n $twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()\n ? 'outbound'\n : 'inbound';\n }\n }\n\n try {\n if ($activity->hasRecordingReasonCompliancePrompted()) {\n $this->logger->info('[Softphone] Request compliance prompt via outbound call', [\n 'activity_uuid' => $activity->getUuid(),\n 'session' => $session->id_string,\n 'conference_sid' => $session->getTelephonyProviderId(),\n ]);\n\n $callInstance = $twilio->createCall(\n $callerId,\n $calleeId,\n $this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [\n 'session' => $session->id_string,\n 'connection' => $calleeConnection->id_string,\n ]),\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $callInstance->sid,\n 'parent_connection_id' => $callerConnection->getId(),\n ]);\n } else {\n $twilioParticipantConnection = $twilio->createParticipantInstance(\n $session->getTelephonyProviderId(),\n $callerId,\n $calleeId,\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $twilioParticipantConnection->callSid,\n ]);\n }\n } catch (RestException $restException) {\n if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {\n throw $restException;\n }\n\n if (! $activity->isInFiniteState()) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n\n $message = __METHOD__ . ': activity.status -> cancelled';\n } else {\n $message = sprintf(\n __METHOD__ . ': not setting to cancelled, activity.status is %s',\n $activity->getStatus()\n );\n }\n\n $this->logger->info($message, [\n 'activity' => $activity->id_string,\n 'exception' => $restException,\n ]);\n }\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n return $this;\n }\n\n private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array\n {\n $session = $connection->getSession();\n\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => false,\n 'record' => false,\n 'waitUrl' => '',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]),\n 'statusCallbackEvent' => [\n 'initiated',\n 'ringing',\n 'answered',\n 'completed',\n ],\n 'statusCallbackMethod' => 'POST',\n 'conferenceStatusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n ]),\n 'conferenceStatusCallbackMethod' => 'POST',\n 'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function applyDefaultTwilioConferenceSettings(Models\\Session $session, array $override = []): array\n {\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => false,\n 'endConferenceOnExit' => false,\n 'waitUrl' => '',\n 'statusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n// 'speaker',\n ]),\n 'statusCallbackMethod' => 'POST',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function joinTwilioConference(Connection $connection, array $options = []): TwiML\\VoiceResponse\n {\n $session = $connection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n\n $this->logger->debug(__METHOD__, [\n 'connection' => $connection->id_string,\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n ]);\n\n $voiceResponse = new TwiML\\VoiceResponse();\n\n $voiceResponse->dial()\n ->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]))\n ->conference(\n $session->getIdString(),\n $this->applyDefaultTwilioConferenceSettings($session, $options)\n );\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'result' => $voiceResponse,\n ]);\n\n return $voiceResponse;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Twilio\\Conference\\ConferenceManager;\n\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Support\\Str;\nuse Jiminny\\Component\\ElasticSearch\\Model\\Observer;\nuse Jiminny\\Component\\Twilio\\Conference\\ConferenceManager;\nuse Jiminny\\Component\\Twilio\\Exception\\OutOfBoundsException;\nuse Jiminny\\Component\\Twilio\\Exception\\UnexpectedValueException;\nuse Jiminny\\Component\\Twilio\\Resolver;\nuse Jiminny\\Component\\Twilio\\VO;\nuse Jiminny\\Component\\UrlGenerator\\Webhook;\nuse Jiminny\\Events;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Services\\ActivityService;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Crm\\CachedCrmServiceDecorator;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Services\\Telephony\\TwilioClientBuilder;\nuse Psr\\Log\\LoggerInterface;\nuse Twilio\\Exceptions\\ConfigurationException;\nuse Twilio\\Exceptions\\RestException;\nuse Twilio\\Exceptions\\TwilioException;\nuse Twilio\\TwiML;\n\nfinal readonly class SoftPhoneManager implements ConferenceManager\n{\n public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;\n public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';\n\n public function __construct(\n private ProviderRegistry $crmProviderRegistry,\n private Webhook $urlGenerator,\n private ActivityService $activityService,\n private LoggerInterface $logger,\n private TwilioClientBuilder $twilioClientBuilder,\n ) {\n }\n\n public function create(VO\\ConferenceManager\\ActivityCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n 'instanceof' => get_class($data),\n ]);\n\n $activity = Observer::skipEvents(function () use ($data) {\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundCreate) {\n $activity = $this->createInboundActivity($data);\n } elseif ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundCreate) {\n $activity = $this->createOutboundActivity($data);\n } else {\n throw new LogicException(sprintf(\n 'Unknown softphone implementation: %s',\n get_class($data)\n ));\n }\n\n return $activity;\n });\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'instanceof' => get_class($data),\n 'activity' => $activity->id_string,\n ]);\n\n return $activity;\n }\n\n private function createOutboundActivity(VO\\ConferenceManager\\SoftPhoneOutboundCreate $data): Models\\Activity\n {\n $startTime = microtime(true);\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n $now = now();\n $user = $data->getUser();\n $team = $user->getTeam();\n $device = $data->getDevice();\n $phoneNumber = $data->getPhoneNumber();\n $route = $data->getPhoneRoute();\n\n if (! $this->activityService->teamCanDial($team, $phoneNumber)) {\n $region = getRegionByNumber($phoneNumber, $user->getCountryCode());\n\n throw new LogicException(sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? 'this number'\n ));\n }\n\n $lead = null;\n $account = null;\n $contact = null;\n $stage = null;\n $opportunity = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $user,\n 'providerSlug' => $team->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n } catch (\\Throwable $throwable) {\n $this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [\n 'exception' => $throwable,\n 'user_id' => $user->getId(),\n 'team_id' => $team->getId(),\n ]);\n $crmService = $this->crmProviderRegistry->get($team->crm->provider);\n if (! $user->isCrmRequired()) {\n $crmService->setUser($user->team->owner);\n } else {\n $crmService->setUser($user);\n }\n }\n\n $prospectId = $data->getProspectId();\n\n try {\n if ($prospectId !== null) {\n $crmStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [\n 'prospect_id' => $prospectId,\n 'object_type' => $data->getObjectType(),\n ]);\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(\n $prospectId,\n $data->getObjectType()\n );\n $this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),\n 'found_lead' => $lead !== null,\n 'found_account' => $account !== null,\n 'found_opportunity' => $opportunity !== null,\n 'found_contact' => $contact !== null,\n ]);\n\n // Try to attach a contact if we have an opportunity.\n if ($opportunity !== null && $contact === null) {\n $matchStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');\n [, , , $contact, ] = $crmService->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n $this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),\n 'found_contact' => $contact !== null,\n ]);\n }\n } else {\n $decorator = resolve(CachedCrmServiceDecorator::class);\n $decorator->setCrmService($crmService);\n\n [$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n }\n } catch (\\Throwable $throwable) {\n $this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.\n }\n\n if ($data->hasProspectStage()) {\n /** @var Models\\Stage $stage */\n $stage = $team->crm->stages()->uuid($data->getProspectStage());\n }\n\n $dbStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting activity creation in DB');\n $activity = $user->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'device_id' => $device->id ?? null,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'value' => $opportunity ? $opportunity->value : null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_PENDING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),\n 'scheduled_start_time' => $now,\n ]);\n $this->logger->info('[SoftPhoneManager] Activity created in DB', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),\n 'activity_id' => $activity->id ?? null,\n ]);\n\n if (! $activity instanceof Models\\Activity) {\n throw new LogicException('Activity model expected');\n }\n\n $regionId = null;\n if ($user->hasRegionId()) {\n $regionId = $user->getRegionId();\n } elseif ($data->hasRegion()) {\n $regionId = $data->getRegion()->getId();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $regionId,\n 'is_muted' => false,\n ]);\n\n $phoneRouteResolver = new Resolver\\PhoneRouteResolver($user, $route);\n $phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();\n $connectionType = $phoneRouteResolver->getConnectionType();\n $callerId = $this->determineUserCallerId($user);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'user_id' => $user->id,\n 'email' => $user->email,\n 'name' => $user->name,\n 'phone_number' => $phoneRouteResolver->mustUsePSTN()\n ? $phoneNumberOrigin\n : $callerId,\n ]);\n\n $caller->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',\n 'phone' => $callerId,\n 'type' => $connectionType,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $phoneNumber,\n 'name' => $activity->prospect_name,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'phone' => $phoneNumber,\n 'direction' => 'outbound-api',\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $activity->refresh();\n\n $this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [\n 'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'activity_id' => $activity->id_string,\n ]);\n $this->logger->debug(__METHOD__ . ': done', [\n 'data' => $data,\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n return $activity;\n }\n\n private function createInboundActivity(VO\\ConferenceManager\\SoftPhoneInboundCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $roomOwner = $data->getUser();\n $team = $roomOwner->getTeam();\n $phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();\n $callerId = $data->getCallerId();\n $callerCallSid = $data->getCallSid();\n\n $now = now();\n\n if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {\n $region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());\n\n throw new LogicException(\n sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? ' this number',\n ),\n );\n }\n\n $lead = $account = $opportunity = $contact = $stage = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $roomOwner->getTeam(),\n 'integrationAdmin' => $roomOwner,\n 'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(\n $callerId,\n null,\n $roomOwner->getId()\n );\n } catch (\\Throwable $throwable) {\n $this->logger->error(__METHOD__ . ': crm exception', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening\n }\n\n /** @var Models\\Activity $activity */\n $activity = $roomOwner->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE_INBOUND,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_RINGING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),\n 'scheduled_start_time' => $now,\n ]);\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $roomOwner->getRegionId(),\n 'is_muted' => false,\n ]);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $callerId,\n 'name' => $activity->prospect_name,\n 'enter_time' => $now,\n ]);\n\n $caller->connections()->create([\n 'telephony_provider_id' => $callerCallSid,\n 'session_id' => $session->getId(),\n 'phone' => $callerId,\n 'direction' => 'inbound',\n 'type' => Connection::TYPE_PSTN,\n 'opened_at' => $now,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'user_id' => $roomOwner->id,\n 'email' => $roomOwner->email,\n 'name' => $roomOwner->name,\n 'phone_number' => $phoneNumber,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => 'outbound-api',\n 'phone' => $phoneNumber,\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'callSid' => $data->getCallSid(),\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n $activity->refresh();\n\n return $activity;\n }\n\n private function getVoiceRecordingReasonCode(Models\\Team $team, string $phoneNumber): int\n {\n $this->logger->debug(__METHOD__, [\n 'team' => $team->id_string,\n 'phone' => $phoneNumber,\n ]);\n\n if (\n ! $team->hasComplianceModeRecordingPrompt()\n && ! $team->hasComplianceModeRecordingRestrictToOneSide()\n && ! $team->hasComplianceModeRecordingRestrictRecording()\n ) {\n return 0;\n }\n\n /** @var Models\\VoiceConsentPrefix|null $voiceConsentPolicy */\n $voiceConsentPolicy = Models\\VoiceConsentPrefix::query()\n ->whereRaw(\"? LIKE CONCAT(destination_prefix, '%')\", [$phoneNumber])\n ->where(static function (Eloquent\\Builder $builder) use ($team): void {\n $builder\n ->whereNull('team_id')\n ->orWhere('team_id', $team->getId());\n })\n ->orderByDesc('team_id')\n ->first();\n\n if (! $voiceConsentPolicy instanceof Models\\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {\n return 0;\n }\n\n if ($team->hasComplianceModeRecordingPrompt()) {\n // This number requires consent from the other party before recording the call.\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;\n }\n\n if ($team->hasComplianceModeRecordingRestrictToOneSide()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;\n }\n\n if ($team->hasComplianceModeRecordingRestrictRecording()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;\n }\n\n return 0;\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOrganiser(Models\\Activity $activity): void\n {\n $this->logger->debug(__METHOD__, [\n 'activity' => $activity->id_string,\n ]);\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $canBeDialed =\n $activity->isTypeSoftPhone()\n && ! $activity->hasStarted()\n && $activity->isPending()\n && $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->exists();\n\n if (! $canBeDialed) {\n throw new OutOfBoundsException('Cannot make an outbound call to this activity.');\n }\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->firstOrFail();\n\n /** @var Models\\Session $session */\n $session = $organiserConnection->getSession();\n\n $twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ]);\n\n // What number will the organizer see as caller id\n $callerId = $roomOwner->getSoftPhoneNumber();\n\n if ($callerId === null) {\n $callee = $activity->participants()->whereNull('user_id')->first();\n // This is a bridge call. We make it look like the callee is calling the organizer\n $callerId = $callee->phone_number;\n }\n\n // What number will the prospect see as caller id\n $calleeId = $organiser->getPhoneNumber();\n\n $this->logger->debug(__METHOD__ . ': twilio call', [\n 'calleeId' => $calleeId,\n 'callerId' => $callerId,\n ]);\n\n $twilioParticipant = $twilio->createParticipantInstance(\n $session->id_string,\n $callerId,\n $calleeId,\n $twilioConferenceSettings\n );\n\n $organiserConnection\n ->setTelephonyProviderId($twilioParticipant->callSid)\n ->save();\n\n $session\n ->setTelephonyProviderId($twilioParticipant->conferenceSid)\n ->save();\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function dialCoach(Models\\Activity $activity, Models\\User $user): void\n {\n throw new \\LogicException('Not implemented yet');\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function terminate(VO\\ConferenceManager\\ActivityTerminate $data): void\n {\n if (! $data instanceof VO\\ConferenceManager\\SoftphoneTerminate) {\n throw new UnexpectedValueException(sprintf(\n 'Expecting an instance of \"%s\", but \"%s\" given.',\n VO\\ConferenceManager\\SoftphoneTerminate::class,\n \\get_class($data)\n ));\n }\n\n $this->logger->debug(__METHOD__, [\n 'activity' => $data->getActivity()->id_string,\n 'user' => $data->getUser()->id_string,\n ]);\n\n $activity = $data->getActivity();\n $roomOwner = $activity->getUser();\n\n $isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();\n\n if ($isCancelling) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions->first();\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->whereIn('type', [\n Connection::TYPE_WEBRTC,\n Connection::TYPE_PSTN,\n ])\n ->firstOrFail();\n\n if (! $organiserConnection->hasTelephonyProviderId()) {\n throw new LogicException(\n 'Unable to terminate call as we do not yet know the organiser telephony provider id',\n );\n }\n\n $twilioClient = $this->twilioClientBuilder->build($activity->getTeam());\n\n $twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());\n\n $session->hasTelephonyProviderId()\n && $twilioClient->terminateConference($session->getTelephonyProviderId());\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function answer(VO\\ConferenceManager\\ActivityAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundAnswer) {\n return $this->answerInboundCall($data);\n }\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundAnswer) {\n return $this->answerOutboundCall($data);\n }\n\n throw new LogicException('Unable to answer - not supported activity answer instance');\n }\n\n private function answerInboundCall(VO\\ConferenceManager\\SoftPhoneInboundAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $dateTime = now();\n\n $activity = $this->create(\n new VO\\ConferenceManager\\SoftPhoneInboundCreate(\n $data->getCallerId(),\n $data->getCallSid(),\n $data->getUser()\n )\n );\n\n /** @var Models\\Participant $activityGuest */\n $activityGuest = $activity->from;\n\n $activityGuest\n ->setEnterTime($dateTime)\n ->save();\n\n /** @var Connection $connection */\n $connection = $activityGuest->connections()\n ->where('type', Connection::TYPE_PSTN)\n ->firstOrFail();\n\n $connection\n ->setTelephonyProviderId($data->getCallSid())\n ->setOpenedAt($dateTime)\n ->save();\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n return $this->joinTwilioConference($connection, [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]);\n }\n\n private function answerOutboundCall(VO\\ConferenceManager\\SoftPhoneOutboundAnswer $data): TwiML\\VoiceResponse\n {\n $connection = $data->getConnection();\n $participant = $connection->getParticipant();\n $activity = $data->getActivity();\n $organiser = $activity->organizer;\n $dateTime = now();\n\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n 'connection' => $connection->id_string,\n 'participant' => $participant->id_string,\n 'activity' => $activity->id_string,\n ]);\n\n if (! $participant->hasEnterTime()) {\n $participant->update([\n 'enter_time' => $dateTime,\n ]);\n }\n\n $connection->update([\n 'is_muted' => $participant->isCoach(),\n 'opened_at' => $dateTime,\n ]);\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n if ($participant->isCoach()) {\n /** @var Connection $roomOwnerConnection */\n $roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'coach' => $roomOwnerConnection->getTelephonyProviderId(),\n ]\n );\n }\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]\n );\n }\n\n private function determineUserCallerId(Models\\User $user): string\n {\n if ($user->hasCallerId()) {\n return $user->getCallerId();\n }\n\n // TODO: We should require a caller id to be verified before using the dialer\n return $user->getSoftPhoneNumber() ?? $user->phone;\n }\n\n private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool\n {\n $startsWith = '[HTTP 404] Unable to create record: The requested resource';\n $endsWith = 'was not found';\n\n return Str::startsWith($exceptionMessage, $startsWith)\n && Str::endsWith($exceptionMessage, $endsWith);\n }\n\n /**\n * @throws RestException\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOtherParty(Connection $callerConnection): self\n {\n $session = $callerConnection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $this->logger->debug(__METHOD__, [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {\n throw new UnexpectedValueException('Unexpected activity type');\n }\n\n $callee = $activity->to;\n $calleeId = $callee->getPhoneNumber();\n\n /** @var Connection $calleeConnection */\n $calleeConnection = $callee->connections()\n ->whereIn('type', [\n Connection::TYPE_PSTN,\n Connection::TYPE_WEBRTC,\n ])\n ->firstOrFail();\n\n $caller = $activity->from;\n\n // we need to take the number from the connection\n // as the one we store on participant level might be the one we call to the rep via PSTN mode\n $callerId = $callerConnection->getPhoneNumber();\n\n if (! $caller->is($callerConnection->getParticipant())) {\n throw new LogicException(sprintf(\n 'Unexpected caller participant. Expected: \"%s\", Actual: \"%s\"',\n $caller->id_string,\n $callerConnection->getParticipant()->id_string\n ));\n }\n\n $twilioConferenceSettings = [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ];\n\n $hasInboundRecording = $activity->isTypeSoftphoneInbound()\n && $roomOwner->checkSoftphoneInboundRecordPreference();\n\n $hasOutboundRecording = $activity->isTypeSoftPhone()\n && $roomOwner->checkSoftphoneOutboundRecordPreference();\n\n $isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;\n\n $isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()\n || $activity->hasRecordingReasonComplianceRestricted();\n\n if ($isRecordingEnabled && ! $isRecordingRestricted) {\n $twilioConferenceSettings = array_merge($twilioConferenceSettings, [\n 'record' => true,\n 'recordingChannels' => 'dual',\n 'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),\n 'recordingStatusCallbackMethod' => 'POST',\n 'recordingStatusCallbackEvent' => 'in-progress completed absent',\n ]);\n\n if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n $twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()\n ? 'outbound'\n : 'inbound';\n }\n }\n\n try {\n if ($activity->hasRecordingReasonCompliancePrompted()) {\n $this->logger->info('[Softphone] Request compliance prompt via outbound call', [\n 'activity_uuid' => $activity->getUuid(),\n 'session' => $session->id_string,\n 'conference_sid' => $session->getTelephonyProviderId(),\n ]);\n\n $callInstance = $twilio->createCall(\n $callerId,\n $calleeId,\n $this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [\n 'session' => $session->id_string,\n 'connection' => $calleeConnection->id_string,\n ]),\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $callInstance->sid,\n 'parent_connection_id' => $callerConnection->getId(),\n ]);\n } else {\n $twilioParticipantConnection = $twilio->createParticipantInstance(\n $session->getTelephonyProviderId(),\n $callerId,\n $calleeId,\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $twilioParticipantConnection->callSid,\n ]);\n }\n } catch (RestException $restException) {\n if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {\n throw $restException;\n }\n\n if (! $activity->isInFiniteState()) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n\n $message = __METHOD__ . ': activity.status -> cancelled';\n } else {\n $message = sprintf(\n __METHOD__ . ': not setting to cancelled, activity.status is %s',\n $activity->getStatus()\n );\n }\n\n $this->logger->info($message, [\n 'activity' => $activity->id_string,\n 'exception' => $restException,\n ]);\n }\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n return $this;\n }\n\n private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array\n {\n $session = $connection->getSession();\n\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => false,\n 'record' => false,\n 'waitUrl' => '',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]),\n 'statusCallbackEvent' => [\n 'initiated',\n 'ringing',\n 'answered',\n 'completed',\n ],\n 'statusCallbackMethod' => 'POST',\n 'conferenceStatusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n ]),\n 'conferenceStatusCallbackMethod' => 'POST',\n 'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function applyDefaultTwilioConferenceSettings(Models\\Session $session, array $override = []): array\n {\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => false,\n 'endConferenceOnExit' => false,\n 'waitUrl' => '',\n 'statusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n// 'speaker',\n ]),\n 'statusCallbackMethod' => 'POST',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function joinTwilioConference(Connection $connection, array $options = []): TwiML\\VoiceResponse\n {\n $session = $connection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n\n $this->logger->debug(__METHOD__, [\n 'connection' => $connection->id_string,\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n ]);\n\n $voiceResponse = new TwiML\\VoiceResponse();\n\n $voiceResponse->dial()\n ->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]))\n ->conference(\n $session->getIdString(),\n $this->applyDefaultTwilioConferenceSettings($session, $options)\n );\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'result' => $voiceResponse,\n ]);\n\n return $voiceResponse;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.42785904,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.43650267,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.4474734,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.45611703,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.46476063,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.47573137,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"bounds":{"left":0.4867021,"top":0.09896249,"width":0.024268618,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"bounds":{"left":0.51329786,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"bounds":{"left":0.5242686,"top":0.09896249,"width":0.029587766,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny_jupiter","depth":4,"bounds":{"left":0.69348407,"top":0.09896249,"width":0.04089096,"height":0.01915403},"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":"19","depth":4,"bounds":{"left":0.6871675,"top":0.123703115,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"19","depth":4,"bounds":{"left":0.6988032,"top":0.123703115,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.71043885,"top":0.123703115,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.72140956,"top":0.12210695,"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.7287234,"top":0.12210695,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3512545928479357484
|
8095951577952526985
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\Twilio\Conference\ConferenceManager;
use Illuminate\Database\Eloquent;
use Illuminate\Support\Str;
use Jiminny\Component\ElasticSearch\Model\Observer;
use Jiminny\Component\Twilio\Conference\ConferenceManager;
use Jiminny\Component\Twilio\Exception\OutOfBoundsException;
use Jiminny\Component\Twilio\Exception\UnexpectedValueException;
use Jiminny\Component\Twilio\Resolver;
use Jiminny\Component\Twilio\VO;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Events;
use Jiminny\Exceptions\LogicException;
use Jiminny\Models;
use Jiminny\Models\Participant\Connection;
use Jiminny\Services\ActivityService;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Crm\CachedCrmServiceDecorator;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Services\Telephony\TwilioClientBuilder;
use Psr\Log\LoggerInterface;
use Twilio\Exceptions\ConfigurationException;
use Twilio\Exceptions\RestException;
use Twilio\Exceptions\TwilioException;
use Twilio\TwiML;
final readonly class SoftPhoneManager implements ConferenceManager
{
public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;
public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';
public function __construct(
private ProviderRegistry $crmProviderRegistry,
private Webhook $urlGenerator,
private ActivityService $activityService,
private LoggerInterface $logger,
private TwilioClientBuilder $twilioClientBuilder,
) {
}
public function create(VO\ConferenceManager\ActivityCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'data' => $data,
'instanceof' => get_class($data),
]);
$activity = Observer::skipEvents(function () use ($data) {
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundCreate) {
$activity = $this->createInboundActivity($data);
} elseif ($data instanceof VO\ConferenceManager\SoftPhoneOutboundCreate) {
$activity = $this->createOutboundActivity($data);
} else {
throw new LogicException(sprintf(
'Unknown softphone implementation: %s',
get_class($data)
));
}
return $activity;
});
$this->logger->debug(__METHOD__ . ': done', [
'instanceof' => get_class($data),
'activity' => $activity->id_string,
]);
return $activity;
}
private function createOutboundActivity(VO\ConferenceManager\SoftPhoneOutboundCreate $data): Models\Activity
{
$startTime = microtime(true);
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
$now = now();
$user = $data->getUser();
$team = $user->getTeam();
$device = $data->getDevice();
$phoneNumber = $data->getPhoneNumber();
$route = $data->getPhoneRoute();
if (! $this->activityService->teamCanDial($team, $phoneNumber)) {
$region = getRegionByNumber($phoneNumber, $user->getCountryCode());
throw new LogicException(sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? 'this number'
));
}
$lead = null;
$account = null;
$contact = null;
$stage = null;
$opportunity = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $user,
'providerSlug' => $team->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
} catch (\Throwable $throwable) {
$this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [
'exception' => $throwable,
'user_id' => $user->getId(),
'team_id' => $team->getId(),
]);
$crmService = $this->crmProviderRegistry->get($team->crm->provider);
if (! $user->isCrmRequired()) {
$crmService->setUser($user->team->owner);
} else {
$crmService->setUser($user);
}
}
$prospectId = $data->getProspectId();
try {
if ($prospectId !== null) {
$crmStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [
'prospect_id' => $prospectId,
'object_type' => $data->getObjectType(),
]);
[$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(
$prospectId,
$data->getObjectType()
);
$this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),
'found_lead' => $lead !== null,
'found_account' => $account !== null,
'found_opportunity' => $opportunity !== null,
'found_contact' => $contact !== null,
]);
// Try to attach a contact if we have an opportunity.
if ($opportunity !== null && $contact === null) {
$matchStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');
[, , , $contact, ] = $crmService->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
$this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),
'found_contact' => $contact !== null,
]);
}
} else {
$decorator = resolve(CachedCrmServiceDecorator::class);
$decorator->setCrmService($crmService);
[$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
}
} catch (\Throwable $throwable) {
$this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.
}
if ($data->hasProspectStage()) {
/** @var Models\Stage $stage */
$stage = $team->crm->stages()->uuid($data->getProspectStage());
}
$dbStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting activity creation in DB');
$activity = $user->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE,
'provider' => Models\Activity::PROVIDER_TWILIO,
'device_id' => $device->id ?? null,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'value' => $opportunity ? $opportunity->value : null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_PENDING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),
'scheduled_start_time' => $now,
]);
$this->logger->info('[SoftPhoneManager] Activity created in DB', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),
'activity_id' => $activity->id ?? null,
]);
if (! $activity instanceof Models\Activity) {
throw new LogicException('Activity model expected');
}
$regionId = null;
if ($user->hasRegionId()) {
$regionId = $user->getRegionId();
} elseif ($data->hasRegion()) {
$regionId = $data->getRegion()->getId();
}
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $regionId,
'is_muted' => false,
]);
$phoneRouteResolver = new Resolver\PhoneRouteResolver($user, $route);
$phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();
$connectionType = $phoneRouteResolver->getConnectionType();
$callerId = $this->determineUserCallerId($user);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'user_id' => $user->id,
'email' => $user->email,
'name' => $user->name,
'phone_number' => $phoneRouteResolver->mustUsePSTN()
? $phoneNumberOrigin
: $callerId,
]);
$caller->connections()->create([
'session_id' => $session->getId(),
'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',
'phone' => $callerId,
'type' => $connectionType,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $phoneNumber,
'name' => $activity->prospect_name,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'phone' => $phoneNumber,
'direction' => 'outbound-api',
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$activity->refresh();
$this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [
'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),
'activity_id' => $activity->id_string,
]);
$this->logger->debug(__METHOD__ . ': done', [
'data' => $data,
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
return $activity;
}
private function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$roomOwner = $data->getUser();
$team = $roomOwner->getTeam();
$phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();
$callerId = $data->getCallerId();
$callerCallSid = $data->getCallSid();
$now = now();
if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {
$region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());
throw new LogicException(
sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? ' this number',
),
);
}
$lead = $account = $opportunity = $contact = $stage = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $roomOwner->getTeam(),
'integrationAdmin' => $roomOwner,
'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
[$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(
$callerId,
null,
$roomOwner->getId()
);
} catch (\Throwable $throwable) {
$this->logger->error(__METHOD__ . ': crm exception', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening
}
/** @var Models\Activity $activity */
$activity = $roomOwner->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE_INBOUND,
'provider' => Models\Activity::PROVIDER_TWILIO,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_RINGING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),
'scheduled_start_time' => $now,
]);
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $roomOwner->getRegionId(),
'is_muted' => false,
]);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $callerId,
'name' => $activity->prospect_name,
'enter_time' => $now,
]);
$caller->connections()->create([
'telephony_provider_id' => $callerCallSid,
'session_id' => $session->getId(),
'phone' => $callerId,
'direction' => 'inbound',
'type' => Connection::TYPE_PSTN,
'opened_at' => $now,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'user_id' => $roomOwner->id,
'email' => $roomOwner->email,
'name' => $roomOwner->name,
'phone_number' => $phoneNumber,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'direction' => 'outbound-api',
'phone' => $phoneNumber,
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$this->logger->debug(__METHOD__ . ': done', [
'callSid' => $data->getCallSid(),
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
$activity->refresh();
return $activity;
}
private function getVoiceRecordingReasonCode(Models\Team $team, string $phoneNumber): int
{
$this->logger->debug(__METHOD__, [
'team' => $team->id_string,
'phone' => $phoneNumber,
]);
if (
! $team->hasComplianceModeRecordingPrompt()
&& ! $team->hasComplianceModeRecordingRestrictToOneSide()
&& ! $team->hasComplianceModeRecordingRestrictRecording()
) {
return 0;
}
/** @var Models\VoiceConsentPrefix|null $voiceConsentPolicy */
$voiceConsentPolicy = Models\VoiceConsentPrefix::query()
->whereRaw("? LIKE CONCAT(destination_prefix, '%')", [$phoneNumber])
->where(static function (Eloquent\Builder $builder) use ($team): void {
$builder
->whereNull('team_id')
->orWhere('team_id', $team->getId());
})
->orderByDesc('team_id')
->first();
if (! $voiceConsentPolicy instanceof Models\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {
return 0;
}
if ($team->hasComplianceModeRecordingPrompt()) {
// This number requires consent from the other party before recording the call.
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;
}
if ($team->hasComplianceModeRecordingRestrictToOneSide()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;
}
if ($team->hasComplianceModeRecordingRestrictRecording()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;
}
return 0;
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOrganiser(Models\Activity $activity): void
{
$this->logger->debug(__METHOD__, [
'activity' => $activity->id_string,
]);
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$canBeDialed =
$activity->isTypeSoftPhone()
&& ! $activity->hasStarted()
&& $activity->isPending()
&& $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->exists();
if (! $canBeDialed) {
throw new OutOfBoundsException('Cannot make an outbound call to this activity.');
}
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->firstOrFail();
/** @var Models\Session $session */
$session = $organiserConnection->getSession();
$twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
]);
// What number will the organizer see as caller id
$callerId = $roomOwner->getSoftPhoneNumber();
if ($callerId === null) {
$callee = $activity->participants()->whereNull('user_id')->first();
// This is a bridge call. We make it look like the callee is calling the organizer
$callerId = $callee->phone_number;
}
// What number will the prospect see as caller id
$calleeId = $organiser->getPhoneNumber();
$this->logger->debug(__METHOD__ . ': twilio call', [
'calleeId' => $calleeId,
'callerId' => $callerId,
]);
$twilioParticipant = $twilio->createParticipantInstance(
$session->id_string,
$callerId,
$calleeId,
$twilioConferenceSettings
);
$organiserConnection
->setTelephonyProviderId($twilioParticipant->callSid)
->save();
$session
->setTelephonyProviderId($twilioParticipant->conferenceSid)
->save();
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'session' => $session->id_string,
]);
}
public function dialCoach(Models\Activity $activity, Models\User $user): void
{
throw new \LogicException('Not implemented yet');
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function terminate(VO\ConferenceManager\ActivityTerminate $data): void
{
if (! $data instanceof VO\ConferenceManager\SoftphoneTerminate) {
throw new UnexpectedValueException(sprintf(
'Expecting an instance of "%s", but "%s" given.',
VO\ConferenceManager\SoftphoneTerminate::class,
\get_class($data)
));
}
$this->logger->debug(__METHOD__, [
'activity' => $data->getActivity()->id_string,
'user' => $data->getUser()->id_string,
]);
$activity = $data->getActivity();
$roomOwner = $activity->getUser();
$isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();
if ($isCancelling) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
}
/** @var Models\Session $session */
$session = $activity->sessions->first();
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->whereIn('type', [
Connection::TYPE_WEBRTC,
Connection::TYPE_PSTN,
])
->firstOrFail();
if (! $organiserConnection->hasTelephonyProviderId()) {
throw new LogicException(
'Unable to terminate call as we do not yet know the organiser telephony provider id',
);
}
$twilioClient = $this->twilioClientBuilder->build($activity->getTeam());
$twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());
$session->hasTelephonyProviderId()
&& $twilioClient->terminateConference($session->getTelephonyProviderId());
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'session' => $session->id_string,
]);
}
public function answer(VO\ConferenceManager\ActivityAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundAnswer) {
return $this->answerInboundCall($data);
}
if ($data instanceof VO\ConferenceManager\SoftPhoneOutboundAnswer) {
return $this->answerOutboundCall($data);
}
throw new LogicException('Unable to answer - not supported activity answer instance');
}
private function answerInboundCall(VO\ConferenceManager\SoftPhoneInboundAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$dateTime = now();
$activity = $this->create(
new VO\ConferenceManager\SoftPhoneInboundCreate(
$data->getCallerId(),
$data->getCallSid(),
$data->getUser()
)
);
/** @var Models\Participant $activityGuest */
$activityGuest = $activity->from;
$activityGuest
->setEnterTime($dateTime)
->save();
/** @var Connection $connection */
$connection = $activityGuest->connections()
->where('type', Connection::TYPE_PSTN)
->firstOrFail();
$connection
->setTelephonyProviderId($data->getCallSid())
->setOpenedAt($dateTime)
->save();
event(new Events\Activities\Connections\Opened($connection));
return $this->joinTwilioConference($connection, [
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]);
}
private function answerOutboundCall(VO\ConferenceManager\SoftPhoneOutboundAnswer $data): TwiML\VoiceResponse
{
$connection = $data->getConnection();
$participant = $connection->getParticipant();
$activity = $data->getActivity();
$organiser = $activity->organizer;
$dateTime = now();
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
'connection' => $connection->id_string,
'participant' => $participant->id_string,
'activity' => $activity->id_string,
]);
if (! $participant->hasEnterTime()) {
$participant->update([
'enter_time' => $dateTime,
]);
}
$connection->update([
'is_muted' => $participant->isCoach(),
'opened_at' => $dateTime,
]);
event(new Events\Activities\Connections\Opened($connection));
if ($participant->isCoach()) {
/** @var Connection $roomOwnerConnection */
$roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();
return $this->joinTwilioConference(
$connection,
[
'coach' => $roomOwnerConnection->getTelephonyProviderId(),
]
);
}
return $this->joinTwilioConference(
$connection,
[
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]
);
}
private function determineUserCallerId(Models\User $user): string
{
if ($user->hasCallerId()) {
return $user->getCallerId();
}
// TODO: We should require a caller id to be verified before using the dialer
return $user->getSoftPhoneNumber() ?? $user->phone;
}
private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool
{
$startsWith = '[HTTP 404] Unable to create record: The requested resource';
$endsWith = 'was not found';
return Str::startsWith($exceptionMessage, $startsWith)
&& Str::endsWith($exceptionMessage, $endsWith);
}
/**
* @throws RestException
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOtherParty(Connection $callerConnection): self
{
$session = $callerConnection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$this->logger->debug(__METHOD__, [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {
throw new UnexpectedValueException('Unexpected activity type');
}
$callee = $activity->to;
$calleeId = $callee->getPhoneNumber();
/** @var Connection $calleeConnection */
$calleeConnection = $callee->connections()
->whereIn('type', [
Connection::TYPE_PSTN,
Connection::TYPE_WEBRTC,
])
->firstOrFail();
$caller = $activity->from;
// we need to take the number from the connection
// as the one we store on participant level might be the one we call to the rep via PSTN mode
$callerId = $callerConnection->getPhoneNumber();
if (! $caller->is($callerConnection->getParticipant())) {
throw new LogicException(sprintf(
'Unexpected caller participant. Expected: "%s", Actual: "%s"',
$caller->id_string,
$callerConnection->getParticipant()->id_string
));
}
$twilioConferenceSettings = [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
];
$hasInboundRecording = $activity->isTypeSoftphoneInbound()
&& $roomOwner->checkSoftphoneInboundRecordPreference();
$hasOutboundRecording = $activity->isTypeSoftPhone()
&& $roomOwner->checkSoftphoneOutboundRecordPreference();
$isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;
$isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()
|| $activity->hasRecordingReasonComplianceRestricted();
if ($isRecordingEnabled && ! $isRecordingRestricted) {
$twilioConferenceSettings = array_merge($twilioConferenceSettings, [
'record' => true,
'recordingChannels' => 'dual',
'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),
'recordingStatusCallbackMethod' => 'POST',
'recordingStatusCallbackEvent' => 'in-progress completed absent',
]);
if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {
$twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()
? 'outbound'
: 'inbound';
}
}
try {
if ($activity->hasRecordingReasonCompliancePrompted()) {
$this->logger->info('[Softphone] Request compliance prompt via outbound call', [
'activity_uuid' => $activity->getUuid(),
'session' => $session->id_string,
'conference_sid' => $session->getTelephonyProviderId(),
]);
$callInstance = $twilio->createCall(
$callerId,
$calleeId,
$this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [
'session' => $session->id_string,
'connection' => $calleeConnection->id_string,
]),
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $callInstance->sid,
'parent_connection_id' => $callerConnection->getId(),
]);
} else {
$twilioParticipantConnection = $twilio->createParticipantInstance(
$session->getTelephonyProviderId(),
$callerId,
$calleeId,
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $twilioParticipantConnection->callSid,
]);
}
} catch (RestException $restException) {
if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {
throw $restException;
}
if (! $activity->isInFiniteState()) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
$message = __METHOD__ . ': activity.status -> cancelled';
} else {
$message = sprintf(
__METHOD__ . ': not setting to cancelled, activity.status is %s',
$activity->getStatus()
);
}
$this->logger->info($message, [
'activity' => $activity->id_string,
'exception' => $restException,
]);
}
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
return $this;
}
private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array
{
$session = $connection->getSession();
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => false,
'record' => false,
'waitUrl' => '',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]),
'statusCallbackEvent' => [
'initiated',
'ringing',
'answered',
'completed',
],
'statusCallbackMethod' => 'POST',
'conferenceStatusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
]),
'conferenceStatusCallbackMethod' => 'POST',
'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function applyDefaultTwilioConferenceSettings(Models\Session $session, array $override = []): array
{
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => false,
'endConferenceOnExit' => false,
'waitUrl' => '',
'statusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
// 'speaker',
]),
'statusCallbackMethod' => 'POST',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function joinTwilioConference(Connection $connection, array $options = []): TwiML\VoiceResponse
{
$session = $connection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$this->logger->debug(__METHOD__, [
'connection' => $connection->id_string,
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
]);
$voiceResponse = new TwiML\VoiceResponse();
$voiceResponse->dial()
->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]))
->conference(
$session->getIdString(),
$this->applyDefaultTwilioConferenceSettings($session, $options)
);
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'result' => $voiceResponse,
]);
return $voiceResponse;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny_jupiter
Sync Changes
Hide This Notification
Code changed:
Hide
19
19
17
Previous Highlighted Error
Next Highlighted Error...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53080
|
NULL
|
0
|
2026-05-18T11:33:19.047829+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103999047_m1.jpg...
|
PhpStorm
|
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Undo
Redo
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Undo","depth":5,"bounds":{"left":0.0,"top":0.0,"width":0.065972224,"height":0.024444444},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Redo","depth":5,"bounds":{"left":0.0,"top":0.0,"width":0.065972224,"height":0.024444444},"on_screen":false,"role_description":"text"}]...
|
4383216283074527733
|
6596065405456956187
|
click
|
hybrid
|
NULL
|
Undo
Redo
SlackFileEditViewGoHistoryWindowHelpEU ( Undo
Redo
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82X t1DOCKER (docker-compose)kibana{"type" : "log""@timestamp": "2026-05-18T11:33:13Z"asticsearch","tags" : ["warning""elh:9200/"},"data"], "pid" :7,'"message": "Unabletoreviveconnection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:13Z", "tags" : ["warning"asticsearch", "data"],"pid":7,"message": "No livingconnections "}I {"type" : "log", "@timestamp" : "2026-05-18T11:33:13Z""tags" : ["error","reporting", "queue-worker""error"], "pid":7,"message" : "mpau4y7h00070bdf86querying failed: Error: No Living connections\nsendReqWithConnection(/usr/share/kibana/node_modules/elasticsearch/src/lib/transport.js:266:15)\n(/usr/share/kibana/node_modules/elasticsearch/src/lib/connection_pool.js:243:7\nprocess._tickCallback (internal/process/next_tick.js:61:11)"}1 {"type" : "log".,"@timestamp": "2026-05-18T11:33:13Z", "tags" : ["error"ticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200*}docker_lamp_1{"type" : "log""@timestamp": "2026-05-18T11:33:15Z","tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message" : "Unablerevive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:15Z" , "tags" : ["warning", "elasticsearch", "data"], "pid":7, "message": "No livingconnections"}kibanains"1 {"type" : "log""@timestamp": "2026-05-18T11:33:15Z""tags" : ["error", "taskManager", "taskManager"], "pid" :7, "message" : "Failed to pollfor work: Error: NoLiving connections"}kibana1 {"type" : "log","@timestamp": "2026-05-18T11:33: 15Z","tags" : ["error", "elasticsearch", "data"], "pid":7, "message":"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}docker_lamp_12026-05-18 11:33:14 Running ['artisan' meeting-bot:schedule-bot] ...3sDONEdocker_lamp_111 '/usr/local/bin/php' 'artisan'meeting-bot: schedule-bot › '/proc/1/fd/1' 2>&1I {"type" : "log", "@timestamp" :"2026-05-18T11:33 :18Z","tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}1 {"type" : "log""@timestamp": "2026-05-18T11:33:18Z", "tags" : ["warning"asticsearch", "data"], "pid":7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:33:18Z", "tags" : ["error", "plug, "taskManager",,"taskManager"],"pid" :7,"message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View ConfigEnable WatchHomeDMsActivityFilesLaterMore-Jiminny ...Metrotrtencee# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario GeorgievNikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiE. Lukas Kovalik y…..AppsJira CloudToastSupport Daily • in 27 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:33:186 0O Files+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53081
|
NULL
|
0
|
2026-05-18T11:33:19.053281+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779103999053_m2.jpg...
|
PhpStorm
|
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Undo
Redo
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Undo","depth":5,"bounds":{"left":0.27027926,"top":1.0,"width":0.03158245,"height":0.0},"on_screen":false,"role_description":"text"},{"role":"AXStaticText","text":"Redo","depth":5,"bounds":{"left":0.27027926,"top":1.0,"width":0.03158245,"height":0.0},"on_screen":false,"role_description":"text"}]...
|
4383216283074527733
|
6596065405456956187
|
click
|
hybrid
|
NULL
|
Undo
Redo
PhpStormViewINavigareCodeLaravelRefactor Undo
Redo
PhpStormViewINavigareCodeLaravelRefactorWindowFV faVsco.js°9 master k© ActivityController.php© SoftPhoneManager.php X=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]& console [PROD]# console [euyCascade>@ ConferenceHandler• M ConferenceManage,© SoftPhoneManager.ong• ConferenceCallbackHandler.phpConferenceManager.php>ODTO•Event> 0 Exceptione Job>W Resolvel07 Service>DVO@ TwilioConstants.ohn© TextMessagingService.phpA console [STAGING] XImplement Trial Ownefinal readonly class SoftPhoneManager implements ConferenceManagerA9.1AYprivate function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\A553Tx: AutovTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE 1D:Go jiminny_jupiter019 A19 V17 ^ Vformat: 'Sorry, dialing %s is not available with your subscription.'' Please contact your manager to enable additional destinations.',-values: Sregion ?? ' this number',556select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team.id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires at IS NULL);• $stage = null:select * from automated_report_results where report id IN (18, 33):© TwilioRepository.phpUoloaden7 UriGeneratorD UtilityM UuidIM Waveformn Wehhooksh Workflow> M ConfiaurationD ConsoleD Commands>@ ActivitiesD) Analytics)mCnlondarevm Crm>C Hubspot>_ IntegrationApptry Somnfesotven = ap( abstret: Crmomenfesot ven: oloss, 1565select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;'providerstug' = Sroomowner-›getTeam()->getCrmConfiguration()->getProviderName(),Somservice + SereResotVver-spreparecmsenvzee 0):568570(seae, fercount, Soportunty, Scontact, Setae) = Semservie-satenpyPhoneselect x Tron aculvity searches Where 10 = 10752select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55results where id IN (81):SroomQwnen->aetIdd7 catch (\Throwable $throwable) &sthis->logger-serror C_METHOD__': Crm exception',l"pycention' => Sthoowahl.p1);573-575- 576=577578579select * trom users where 1d IN 00055,15787,11 985071Fixina Redis Rate Limit Erronselect * trom users where qroup 10 IN (5/00:SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;Uinde+00select * from teams:Local ChangesLog xChanaes 5 files=.env.local appJiminnyDebugCommand.php app/Console/Commandsphploaqina.ono confidSoftPhoneManager.php app/Component/Twilio/Conference/ConferenceMar(C) TextMessacinaService.oho aoo/Services/Teleohonv~ Unversioned Files 9 files= env.nikillocal anniE.env.other app© CanAccessAiReportsTest.php tests/Unit/Policies© CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Rio) favicon.ico nublidE ids.txt appTraw_sqL_query.sql [EMAIL] app/Console/Commands/Crm/HubspotM+ WEBHOOK_FILTERING_IMPLEMENTATION.md app<→ E, Side-by-side viewer +Do not ianore y@cb4ebf0c config/logging.phpCurrent vercion'driver' => 'errorlog'Idnivent =s lennonloglraste and maich stvleSelect AllOpen DevloolsLevel → env Lub LCVEL LITO)'path' => storage_path('Logs/Laravel. Log'),'level' => env('LOG_LEVEL', 'info').'path' => storage_path('logs/Laravel.log')'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.l0g')'level' => env('LOG LEVEL', 'info').• suppont Dally • In 21m100% S2• Mon 18 May 14:33:18AskJiminnyReportActivityServiceTest vFixing Redis Rate LimSalestorce Token FalliCascade+0 ..Wcascade Code x• ..Kick off a new proiect. Make changes1ditterenceW Windsurf Teams 328:49 UTF-8 P 4 spaces ®...
|
53078
|
NULL
|
NULL
|
NULL
|
|
53082
|
1860
|
0
|
2026-05-18T11:33:50.084264+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104030084_m1.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴2XIDOCKER (docker-compose)kibana{"type""log""@timestamp": "2026-05-18T11:33:43Z"ticsearch""data"],"pid" :7,message","tags" : ["error""elas:"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}kibana1 {"type" : "log""@timestamp" : "2026-05-18T11:33:45Z", "tags" : ["warning"asticsearch", "data"], "pid" :7,,"el'message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:33:45Z", "tags" : ["warning"asticsearch", "data"],'"message"kibana1 {"type" : "log"connections"}"@timestamp""2026-05-18T11:33:45Z""tags" : ["error"ins", "taskManager"Living connections"}"taskManager"], "pid":7, "message":"Failed to poll for work: Error: Nokibana1 {"type" : "log","@timestamp" : "2026-05-18T11:33:45Z", "tags" : ["error","elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch: 9200*}kibanaticsearch"1 {"type": "log""@timestamp": "2026-05-18T11:33:48Z", "tags" : ["error", "elas, "data"],"pid":7, "message":"[ConnectionError]:getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}kibana1 {"type": "log","@timestamp" : "2026-05-18T11:33:48Z""tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:48Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:33:48Z","tags" : ["error","taskManager", "taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLiving connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:33:49Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:49Z", "tags" : ["warning", "elasticsearch", "data"],"pid":7, "message": "No living connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:33:49Z""tags" : ["warning"ugins","licensing"], "pid" :7, "message" : "License information could not be obtained from Elasticsearch due to Error: No Living connections error"}1 {"type" : "log","@timestamp": "2026-05-18T11:33:49Z", "tags" : ["warning"asticsearch", "monitoring"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:33:49Z", "tags" : ["warning", "elasticsearch", "monitoring"],"pid":7, "message": "No living connections"}1 {"type": "log", "@timestamp": "2026-05-18T11:33:49Z","tags" : ["warning", "plugins","licensing"],"pid":7, "message": "License information could not be obtained from Elasticsearch due to Error: No Living connectionsView in Docker Desktop• View ConfigEnable Watch8• Mon 18 May 14:33:49Jiminny ...Metrorrtence# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...• Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov ". Mario GeorgievNikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 27 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas@ Files• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:6 0+HomeDMsActivityFilesLaterMoreSign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMгТОТОвОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
-269239859886645834
|
NULL
|
idle
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴2XIDOCKER (docker-compose)kibana{"type""log""@timestamp": "2026-05-18T11:33:43Z"ticsearch""data"],"pid" :7,message","tags" : ["error""elas:"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}kibana1 {"type" : "log""@timestamp" : "2026-05-18T11:33:45Z", "tags" : ["warning"asticsearch", "data"], "pid" :7,,"el'message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:33:45Z", "tags" : ["warning"asticsearch", "data"],'"message"kibana1 {"type" : "log"connections"}"@timestamp""2026-05-18T11:33:45Z""tags" : ["error"ins", "taskManager"Living connections"}"taskManager"], "pid":7, "message":"Failed to poll for work: Error: Nokibana1 {"type" : "log","@timestamp" : "2026-05-18T11:33:45Z", "tags" : ["error","elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch: 9200*}kibanaticsearch"1 {"type": "log""@timestamp": "2026-05-18T11:33:48Z", "tags" : ["error", "elas, "data"],"pid":7, "message":"[ConnectionError]:getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}kibana1 {"type": "log","@timestamp" : "2026-05-18T11:33:48Z""tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:48Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:33:48Z","tags" : ["error","taskManager", "taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLiving connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:33:49Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:33:49Z", "tags" : ["warning", "elasticsearch", "data"],"pid":7, "message": "No living connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:33:49Z""tags" : ["warning"ugins","licensing"], "pid" :7, "message" : "License information could not be obtained from Elasticsearch due to Error: No Living connections error"}1 {"type" : "log","@timestamp": "2026-05-18T11:33:49Z", "tags" : ["warning"asticsearch", "monitoring"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:33:49Z", "tags" : ["warning", "elasticsearch", "monitoring"],"pid":7, "message": "No living connections"}1 {"type": "log", "@timestamp": "2026-05-18T11:33:49Z","tags" : ["warning", "plugins","licensing"],"pid":7, "message": "License information could not be obtained from Elasticsearch due to Error: No Living connectionsView in Docker Desktop• View ConfigEnable Watch8• Mon 18 May 14:33:49Jiminny ...Metrorrtence# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...• Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov ". Mario GeorgievNikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 27 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas@ Files• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:6 0+HomeDMsActivityFilesLaterMoreSign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMгТОТОвОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
53080
|
NULL
|
NULL
|
NULL
|
|
53083
|
1861
|
0
|
2026-05-18T11:33:50.986636+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104030986_m2.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhpStormProiect vViewINavicareCodeLaravelRefactorW PhpStormProiect vViewINavicareCodeLaravelRefactorWindowFV faVsco.js°9 master k© ActivityController.phpC BaseService.php© SoftPhoneManager.php X=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]>@ ConferenceHandlerv M Conterencemanage,© TextMessagingService.phpA console [STAGING] X© SoftPhoneManager.ong• ConferenceCallbackHandler.php© ConferenceManager.php>ODTO•Event> 0 Exceptione Job>W ResolvelW Service,DVO@ TwilioConstants.ohnfinal readonly class SoftPhoneManager implements ConferenceManagerA9.1AYprivate function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\A553Tx: AutovTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE WD:format: 'Sorry, dialing %s is not available with your subscription.'' Please contact your manager to enable additional destinations.',"Values: Sregion ??' this number',556select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now) OR r.expires_at IS NULL);= $contact = $stage = null;select * from automated_report_results where report id IN (18, 33):© TwilioRepository.phpUoloaden7 UrlGeneratorD UtilityM UuidIM Waveformn Wehhooksh Workflow> M ConfiaurationD ConsoleD Commands>@ ActivitiesD) Analytics)mCnlondarevm Crm>C Hubspot› IntegrationApptry Somnfesotven = ap( abstret: Crmomenfesot ven: oloss, 1'team' => SroomOwner->getTeam(),565select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;|'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),Somservice + SereResotVver-spreparecmsenvzee 0):568570(82e0d, Beriount, Sopporetunaty, Scontact, Sstage) e Sornßervite-natenByPhoneselect x Tron aculvity searches where 10 = 10752select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55results where id IN (81):SroomQwnen->aetIdd329} catch (\Throwable $throwable) {sthis->logger-serror C_METHOD__': Crm exception',l"pycention' => Sthoowahl.p1);573-575- 576=577578579select * trom users where 1d IN 00055,15787,11985011select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:Local ChangesLog xChanaes 5 filesE env.local appJiminnyDebugCommand.php app/Console/Commandsphploaqina.ono confidSoftPhoneManager.php app/Component/Twilio/Conference/ConferenceMar(C) TextMessacinaService.oho aoo/Services/Telephonv~ Unversioned Files 9 files= env.nikillocal anniE.env.other app© CanAccessAiReportsTest.php tests/Unit/Policies@ CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Ro) favicon.ico nublidE ids.txt appTraw_sqL_query.sql [EMAIL] app/Console/Commands/Crm/HubspotM+ WEBHOOK_FILTERING_IMPLEMENTATION.md app<→ E, Side-by-side viewer +Do not ianore y@cb4ebf0c config/logging.phpCurrent vercion'driver' => 'errorlog'Level → env "Lub Level', "1nto)'path' => storage_path('logs/laravel.log'),'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.l0g')'level' => env('LOG LEVEL', 'info').• suppont Dally • In 21m100% 2• Mon 18 May 14:33:50AskJiminnyReportActivityServiceTest v& console [PROD]# console [euyCascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliEnable SClM Role Mi+0 ..Go jiminny_jupiterrole/permission system to understand the architecture.019 A19 V17 ^ VSearched recorder.*voice|analyst in ~/jiminny/appThought for 1s>Created Todo List 7 tasks1)Analvze current SCIM imolementation and role/permission svstem'Review validation rules for roles and permissions• Design SCIM role assignment schema• Implement role mutator for SCIM• Add validation to SCIM role assignment• Write unit tests for SCIM role managementUpdate scim documentationIhought for 1shought for ZsRead CoreUserRequest.php and 2 other filesSearched ROLE RECORDERROLE VOICEROLE ANALYSTIROLE LISTENER in apo/Models/User.ohoSurfinaAsk anvthina (%4L)+ @code SWE-1.60901ditterenceW Windsurf Toams 228-10 UTF.8io 4 spaces ©...
|
NULL
|
-2077798200752967274
|
NULL
|
idle
|
ocr
|
NULL
|
PhpStormProiect vViewINavicareCodeLaravelRefactorW PhpStormProiect vViewINavicareCodeLaravelRefactorWindowFV faVsco.js°9 master k© ActivityController.phpC BaseService.php© SoftPhoneManager.php X=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]>@ ConferenceHandlerv M Conterencemanage,© TextMessagingService.phpA console [STAGING] X© SoftPhoneManager.ong• ConferenceCallbackHandler.php© ConferenceManager.php>ODTO•Event> 0 Exceptione Job>W ResolvelW Service,DVO@ TwilioConstants.ohnfinal readonly class SoftPhoneManager implements ConferenceManagerA9.1AYprivate function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\A553Tx: AutovTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE WD:format: 'Sorry, dialing %s is not available with your subscription.'' Please contact your manager to enable additional destinations.',"Values: Sregion ??' this number',556select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now) OR r.expires_at IS NULL);= $contact = $stage = null;select * from automated_report_results where report id IN (18, 33):© TwilioRepository.phpUoloaden7 UrlGeneratorD UtilityM UuidIM Waveformn Wehhooksh Workflow> M ConfiaurationD ConsoleD Commands>@ ActivitiesD) Analytics)mCnlondarevm Crm>C Hubspot› IntegrationApptry Somnfesotven = ap( abstret: Crmomenfesot ven: oloss, 1'team' => SroomOwner->getTeam(),565select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;|'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),Somservice + SereResotVver-spreparecmsenvzee 0):568570(82e0d, Beriount, Sopporetunaty, Scontact, Sstage) e Sornßervite-natenByPhoneselect x Tron aculvity searches where 10 = 10752select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55results where id IN (81):SroomQwnen->aetIdd329} catch (\Throwable $throwable) {sthis->logger-serror C_METHOD__': Crm exception',l"pycention' => Sthoowahl.p1);573-575- 576=577578579select * trom users where 1d IN 00055,15787,11985011select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:Local ChangesLog xChanaes 5 filesE env.local appJiminnyDebugCommand.php app/Console/Commandsphploaqina.ono confidSoftPhoneManager.php app/Component/Twilio/Conference/ConferenceMar(C) TextMessacinaService.oho aoo/Services/Telephonv~ Unversioned Files 9 files= env.nikillocal anniE.env.other app© CanAccessAiReportsTest.php tests/Unit/Policies@ CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Ro) favicon.ico nublidE ids.txt appTraw_sqL_query.sql [EMAIL] app/Console/Commands/Crm/HubspotM+ WEBHOOK_FILTERING_IMPLEMENTATION.md app<→ E, Side-by-side viewer +Do not ianore y@cb4ebf0c config/logging.phpCurrent vercion'driver' => 'errorlog'Level → env "Lub Level', "1nto)'path' => storage_path('logs/laravel.log'),'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.l0g')'level' => env('LOG LEVEL', 'info').• suppont Dally • In 21m100% 2• Mon 18 May 14:33:50AskJiminnyReportActivityServiceTest v& console [PROD]# console [euyCascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliEnable SClM Role Mi+0 ..Go jiminny_jupiterrole/permission system to understand the architecture.019 A19 V17 ^ VSearched recorder.*voice|analyst in ~/jiminny/appThought for 1s>Created Todo List 7 tasks1)Analvze current SCIM imolementation and role/permission svstem'Review validation rules for roles and permissions• Design SCIM role assignment schema• Implement role mutator for SCIM• Add validation to SCIM role assignment• Write unit tests for SCIM role managementUpdate scim documentationIhought for 1shought for ZsRead CoreUserRequest.php and 2 other filesSearched ROLE RECORDERROLE VOICEROLE ANALYSTIROLE LISTENER in apo/Models/User.ohoSurfinaAsk anvthina (%4L)+ @code SWE-1.60901ditterenceW Windsurf Toams 228-10 UTF.8io 4 spaces ©...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53084
|
1860
|
1
|
2026-05-18T11:34:22.414061+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104062414_m1.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
1
Previous 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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","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":"9","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","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}]...
|
7427406631313725685
|
-8132367010325492798
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
1
Previous Highlighted Error
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DOCKER (docker-compose)asticsearch"h:9200/"3, "data"],"pid":7,"message": "UnableDEV (docker)₴2reviveconnection: [URL_WITH_CREDENTIALS] "2026-05-18T11:34:18Z" , "tags" : ["warning"asticsearch", "data"], "pid" :7,"message" : "No livingconnections"}1 {"type": "log"!,"@timestamp": "2026-05-18T11:34:18Z","taskManager""taskManager"], "pid" :7, "message" : "Failed to pollfor work: Error: NoLivingconnections"}kibana{"type": "log""@timestamp": "2026-05-18T11:34:19Z","tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message" : "Unabletorevive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:34: 19Z"asticsearch", "data"],'"pid":7, "message" : "No livingconnections"},"tags" : ["warning", "elkibana{"type" : "log", "@timestamp": "2026-05-18T11:34:19Z", "tags" : ["warning", "plugins""licensing"],"pid":7, "message": "License informationcould not be obtained from Elasticsearch due to Error: No Living connectionskibana1 {"type" : "log""@timestamp": "2026-05-18T11:34:19Z","tags" : ["warning"asticsearch", "monitoring"], "pid" :7, "message" : "Unable tocsearch:9200/"3revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:34:20Z","tags" : ["error""elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch: 9200"}kibanaI {"type" : "log", "@timestamp" : "2026-05-18T11:34:21Z""tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}kibana1 {"type" : "log","@timestamp": "2026-05-18T11:34:21Z","tags" : ["warning""elasticsearch", "data"], "pid" :7,"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:34:21Z" , "tags" : ["warning" , "elasticsearch", "data"], "pid":7, "message": "No living connections"}kibana1 {"type" : "log","@timestamp" : "2026-05-18T11:34:21Z", "tags" : ["error", "plugins",, "taskManager",, "taskManager"], "pid" :7, "message" : "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View ConfigEnable Watch•HomeDMsActivityFilesLaterMoreJiminny ...MetromrtencE# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka StoyanovaStoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov ". Mario GeorgievNikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiE. Lukas Kovalik y...AppsJira CloudToastSupport Daily • in 26 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas@ Files• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:34:226 0+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53085
|
1861
|
1
|
2026-05-18T11:34:23.808360+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104063808_m2.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9...
|
[{"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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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":"9","depth":4,"bounds":{"left":0.3929521,"top":0.10055866,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"}]...
|
5774539588715192368
|
-8132367147898524734
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
PhpStormProiect vViewINavicareCodeLaravelRefactorWindowFV faVsco.js°9 master k© ActivityController.phpC BaseService.php© SoftPhoneManager.php X=custom.log= laravel.log« SF jiminny@localhost]4 HS_local [jiminny@localhost]& console [PROD]& console [EU]>@ ConferenceHandlerv M Conterencemanage,© SoftPhoneManager.ong• ConferenceCallbackHandler.php© ConferenceManager.php>ODTO•Event> 0 Exceptione Job>W ResolvelW Service>DVO@ TwilioConstants.ohn© TextMessagingService.phpA console [STAGING] Xfinal readonly class SoftPhoneManager implements ConferenceManagerA9.1AYprivate function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\A553Tx: AutovTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE 1D:Go jiminny_jupiter019 A19 V17 ^ Vformat: 'Sorry, dialing %s is not available with your subscription.'' Please contact your manager to enable additional destinations.',"Values: Sregion ??' this number',556select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now) OR r.expires_at IS NULL);= $contact = $stage = null;select * from automated_report_results where report id IN (18, 33):© TwilioRepository.phpUoloaden7 UrlGeneratorD UtilityM UuidN Waveformn Wehhooksh Workflow> M ConfiaurationD ConsoleD Commands>@ ActivitiesD) Analytics)mCnlondarevm Crm>C Hubspot› IntegrationApptry Somnfesotven = ap( abstret: Crmomenfesot ven: oloss, 1'team' => SroomOwner->getTeam(),565select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;|'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),Somservice + SereResotVver-spreparecmsenvzee 0):568570(82e0d, Beriount, Sopporetunaty, Scontact, Sstage) e Sornßervite-natenByPhoneselect x rron aculvity searches where 10 = 10702select * from activity search filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55results where id IN (81):SroomQwnen->aetIdd329} catch (\Throwable $throwable) {sthis->logger-serror C_METHOD__': Crm exception',l"pycention' => Sthoowahl.p1);573-575- 576=577578579select * trom users where 1d IN 00055,15787,11 985071select * trom users where qroup 10 In (5/00:SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:Local ChangesLog xChanaes 5 filesE env.local appJiminnyDebugCommand.php app/Console/Commandsphploaqina.ono confidSoftPhoneManager.php app/Component/Twilio/Conference/ConferenceMar(C) TextMessacinaService.oho aoo/Services/Telephonv~ Unversioned Files 9 files= env.nikillocal anniE.env.other app© CanAccessAiReportsTest.php tests/Unit/Policies@ CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Ro) favicon.ico nublidE ids.txt appTraw_sqL_query.sql [EMAIL] app/Console/Commands/Crm/HubspotM+ WEBHOOK_FILTERING_IMPLEMENTATION.md app<→ E, Side-by-side viewer +Do not ianore y@cb4ebf0c config/logging.phpCurrent vercion'driver' => 'errorlog'Level → env "Lub Level', "1nto)'path' => storage_path('logs/laravel.1og'),'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').• suppont Dally • In 20m100% 2• Mon 18 May 14:34:23AskJiminnyReportActivityServiceTest vCascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..sedrcned class. Kole in ap.inougntror is1Analyze current selm implementation and role permission systemReview validation rules for roles and permissionsiDesign SCIM role assignment schemaImolement role mutator for SCIMAdd validation to SCIM role assianmentWrite unit tests for SCIM role managementUodate SCIM documentationkead scimProvisioning.onp and s otnertlles >Thought for 1s)Thought for 2sSearched ROLE_RECORDER|ROLE_VOICEIROLE ANALYST|ROLE LISTENER in app/Models/User.phpThought for 2sSearched PERMISSION ADMINPERMISSION MANAGER In app/ModelsRead RolesAndCrmOntionMismatchRule.ohdAnalyzed app/Component/SCIM/Mutators/Attributes/UserThought for 1s)Ask anvthina (%4L)+ <>Code sws.e1ditterenceWAAYWN Windsurf Toams 228-10 UTF.8io 4 spaces...
|
53083
|
NULL
|
NULL
|
NULL
|
|
53086
|
1860
|
2
|
2026-05-18T11:34:53.832131+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104093832_m1.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\Twilio\Conference\ConferenceManager;
use Illuminate\Database\Eloquent;
use Illuminate\Support\Str;
use Jiminny\Component\ElasticSearch\Model\Observer;
use Jiminny\Component\Twilio\Conference\ConferenceManager;
use Jiminny\Component\Twilio\Exception\OutOfBoundsException;
use Jiminny\Component\Twilio\Exception\UnexpectedValueException;
use Jiminny\Component\Twilio\Resolver;
use Jiminny\Component\Twilio\VO;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Events;
use Jiminny\Exceptions\LogicException;
use Jiminny\Models;
use Jiminny\Models\Participant\Connection;
use Jiminny\Services\ActivityService;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Crm\CachedCrmServiceDecorator;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Services\Telephony\TwilioClientBuilder;
use Psr\Log\LoggerInterface;
use Twilio\Exceptions\ConfigurationException;
use Twilio\Exceptions\RestException;
use Twilio\Exceptions\TwilioException;
use Twilio\TwiML;
final readonly class SoftPhoneManager implements ConferenceManager
{
public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;
public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';
public function __construct(
private ProviderRegistry $crmProviderRegistry,
private Webhook $urlGenerator,
private ActivityService $activityService,
private LoggerInterface $logger,
private TwilioClientBuilder $twilioClientBuilder,
) {
}
public function create(VO\ConferenceManager\ActivityCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'data' => $data,
'instanceof' => get_class($data),
]);
$activity = Observer::skipEvents(function () use ($data) {
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundCreate) {
$activity = $this->createInboundActivity($data);
} elseif ($data instanceof VO\ConferenceManager\SoftPhoneOutboundCreate) {
$activity = $this->createOutboundActivity($data);
} else {
throw new LogicException(sprintf(
'Unknown softphone implementation: %s',
get_class($data)
));
}
return $activity;
});
$this->logger->debug(__METHOD__ . ': done', [
'instanceof' => get_class($data),
'activity' => $activity->id_string,
]);
return $activity;
}
private function createOutboundActivity(VO\ConferenceManager\SoftPhoneOutboundCreate $data): Models\Activity
{
$startTime = microtime(true);
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
$now = now();
$user = $data->getUser();
$team = $user->getTeam();
$device = $data->getDevice();
$phoneNumber = $data->getPhoneNumber();
$route = $data->getPhoneRoute();
if (! $this->activityService->teamCanDial($team, $phoneNumber)) {
$region = getRegionByNumber($phoneNumber, $user->getCountryCode());
throw new LogicException(sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? 'this number'
));
}
$lead = null;
$account = null;
$contact = null;
$stage = null;
$opportunity = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $user,
'providerSlug' => $team->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
} catch (\Throwable $throwable) {
$this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [
'exception' => $throwable,
'user_id' => $user->getId(),
'team_id' => $team->getId(),
]);
$crmService = $this->crmProviderRegistry->get($team->crm->provider);
if (! $user->isCrmRequired()) {
$crmService->setUser($user->team->owner);
} else {
$crmService->setUser($user);
}
}
$prospectId = $data->getProspectId();
try {
if ($prospectId !== null) {
$crmStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [
'prospect_id' => $prospectId,
'object_type' => $data->getObjectType(),
]);
[$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(
$prospectId,
$data->getObjectType()
);
$this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),
'found_lead' => $lead !== null,
'found_account' => $account !== null,
'found_opportunity' => $opportunity !== null,
'found_contact' => $contact !== null,
]);
// Try to attach a contact if we have an opportunity.
if ($opportunity !== null && $contact === null) {
$matchStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');
[, , , $contact, ] = $crmService->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
$this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),
'found_contact' => $contact !== null,
]);
}
} else {
$decorator = resolve(CachedCrmServiceDecorator::class);
$decorator->setCrmService($crmService);
[$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
}
} catch (\Throwable $throwable) {
$this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.
}
if ($data->hasProspectStage()) {
/** @var Models\Stage $stage */
$stage = $team->crm->stages()->uuid($data->getProspectStage());
}
$dbStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting activity creation in DB');
$activity = $user->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE,
'provider' => Models\Activity::PROVIDER_TWILIO,
'device_id' => $device->id ?? null,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'value' => $opportunity ? $opportunity->value : null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_PENDING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),
'scheduled_start_time' => $now,
]);
$this->logger->info('[SoftPhoneManager] Activity created in DB', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),
'activity_id' => $activity->id ?? null,
]);
if (! $activity instanceof Models\Activity) {
throw new LogicException('Activity model expected');
}
$regionId = null;
if ($user->hasRegionId()) {
$regionId = $user->getRegionId();
} elseif ($data->hasRegion()) {
$regionId = $data->getRegion()->getId();
}
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $regionId,
'is_muted' => false,
]);
$phoneRouteResolver = new Resolver\PhoneRouteResolver($user, $route);
$phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();
$connectionType = $phoneRouteResolver->getConnectionType();
$callerId = $this->determineUserCallerId($user);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'user_id' => $user->id,
'email' => $user->email,
'name' => $user->name,
'phone_number' => $phoneRouteResolver->mustUsePSTN()
? $phoneNumberOrigin
: $callerId,
]);
$caller->connections()->create([
'session_id' => $session->getId(),
'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',
'phone' => $callerId,
'type' => $connectionType,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $phoneNumber,
'name' => $activity->prospect_name,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'phone' => $phoneNumber,
'direction' => 'outbound-api',
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$activity->refresh();
$this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [
'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),
'activity_id' => $activity->id_string,
]);
$this->logger->debug(__METHOD__ . ': done', [
'data' => $data,
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
return $activity;
}
private function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$roomOwner = $data->getUser();
$team = $roomOwner->getTeam();
$phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();
$callerId = $data->getCallerId();
$callerCallSid = $data->getCallSid();
$now = now();
if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {
$region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());
throw new LogicException(
sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? ' this number',
),
);
}
$lead = $account = $opportunity = $contact = $stage = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $roomOwner->getTeam(),
'integrationAdmin' => $roomOwner,
'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
[$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(
$callerId,
null,
$roomOwner->getId()
);
} catch (\Throwable $throwable) {
$this->logger->error(__METHOD__ . ': crm exception', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening
}
/** @var Models\Activity $activity */
$activity = $roomOwner->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE_INBOUND,
'provider' => Models\Activity::PROVIDER_TWILIO,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_RINGING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),
'scheduled_start_time' => $now,
]);
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $roomOwner->getRegionId(),
'is_muted' => false,
]);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $callerId,
'name' => $activity->prospect_name,
'enter_time' => $now,
]);
$caller->connections()->create([
'telephony_provider_id' => $callerCallSid,
'session_id' => $session->getId(),
'phone' => $callerId,
'direction' => 'inbound',
'type' => Connection::TYPE_PSTN,
'opened_at' => $now,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'user_id' => $roomOwner->id,
'email' => $roomOwner->email,
'name' => $roomOwner->name,
'phone_number' => $phoneNumber,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'direction' => 'outbound-api',
'phone' => $phoneNumber,
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$this->logger->debug(__METHOD__ . ': done', [
'callSid' => $data->getCallSid(),
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
$activity->refresh();
return $activity;
}
private function getVoiceRecordingReasonCode(Models\Team $team, string $phoneNumber): int
{
$this->logger->debug(__METHOD__, [
'team' => $team->id_string,
'phone' => $phoneNumber,
]);
if (
! $team->hasComplianceModeRecordingPrompt()
&& ! $team->hasComplianceModeRecordingRestrictToOneSide()
&& ! $team->hasComplianceModeRecordingRestrictRecording()
) {
return 0;
}
/** @var Models\VoiceConsentPrefix|null $voiceConsentPolicy */
$voiceConsentPolicy = Models\VoiceConsentPrefix::query()
->whereRaw("? LIKE CONCAT(destination_prefix, '%')", [$phoneNumber])
->where(static function (Eloquent\Builder $builder) use ($team): void {
$builder
->whereNull('team_id')
->orWhere('team_id', $team->getId());
})
->orderByDesc('team_id')
->first();
if (! $voiceConsentPolicy instanceof Models\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {
return 0;
}
if ($team->hasComplianceModeRecordingPrompt()) {
// This number requires consent from the other party before recording the call.
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;
}
if ($team->hasComplianceModeRecordingRestrictToOneSide()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;
}
if ($team->hasComplianceModeRecordingRestrictRecording()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;
}
return 0;
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOrganiser(Models\Activity $activity): void
{
$this->logger->debug(__METHOD__, [
'activity' => $activity->id_string,
]);
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$canBeDialed =
$activity->isTypeSoftPhone()
&& ! $activity->hasStarted()
&& $activity->isPending()
&& $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->exists();
if (! $canBeDialed) {
throw new OutOfBoundsException('Cannot make an outbound call to this activity.');
}
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->firstOrFail();
/** @var Models\Session $session */
$session = $organiserConnection->getSession();
$twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
]);
// What number will the organizer see as caller id
$callerId = $roomOwner->getSoftPhoneNumber();
if ($callerId === null) {
$callee = $activity->participants()->whereNull('user_id')->first();
// This is a bridge call. We make it look like the callee is calling the organizer
$callerId = $callee->phone_number;
}
// What number will the prospect see as caller id
$calleeId = $organiser->getPhoneNumber();
$this->logger->debug(__METHOD__ . ': twilio call', [
'calleeId' => $calleeId,
'callerId' => $callerId,
]);
$twilioParticipant = $twilio->createParticipantInstance(
$session->id_string,
$callerId,
$calleeId,
$twilioConferenceSettings
);
$organiserConnection
->setTelephonyProviderId($twilioParticipant->callSid)
->save();
$session
->setTelephonyProviderId($twilioParticipant->conferenceSid)
->save();
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'session' => $session->id_string,
]);
}
public function dialCoach(Models\Activity $activity, Models\User $user): void
{
throw new \LogicException('Not implemented yet');
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function terminate(VO\ConferenceManager\ActivityTerminate $data): void
{
if (! $data instanceof VO\ConferenceManager\SoftphoneTerminate) {
throw new UnexpectedValueException(sprintf(
'Expecting an instance of "%s", but "%s" given.',
VO\ConferenceManager\SoftphoneTerminate::class,
\get_class($data)
));
}
$this->logger->debug(__METHOD__, [
'activity' => $data->getActivity()->id_string,
'user' => $data->getUser()->id_string,
]);
$activity = $data->getActivity();
$roomOwner = $activity->getUser();
$isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();
if ($isCancelling) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
}
/** @var Models\Session $session */
$session = $activity->sessions->first();
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->whereIn('type', [
Connection::TYPE_WEBRTC,
Connection::TYPE_PSTN,
])
->firstOrFail();
if (! $organiserConnection->hasTelephonyProviderId()) {
throw new LogicException(
'Unable to terminate call as we do not yet know the organiser telephony provider id',
);
}
$twilioClient = $this->twilioClientBuilder->build($activity->getTeam());
$twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());
$session->hasTelephonyProviderId()
&& $twilioClient->terminateConference($session->getTelephonyProviderId());
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'session' => $session->id_string,
]);
}
public function answer(VO\ConferenceManager\ActivityAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundAnswer) {
return $this->answerInboundCall($data);
}
if ($data instanceof VO\ConferenceManager\SoftPhoneOutboundAnswer) {
return $this->answerOutboundCall($data);
}
throw new LogicException('Unable to answer - not supported activity answer instance');
}
private function answerInboundCall(VO\ConferenceManager\SoftPhoneInboundAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$dateTime = now();
$activity = $this->create(
new VO\ConferenceManager\SoftPhoneInboundCreate(
$data->getCallerId(),
$data->getCallSid(),
$data->getUser()
)
);
/** @var Models\Participant $activityGuest */
$activityGuest = $activity->from;
$activityGuest
->setEnterTime($dateTime)
->save();
/** @var Connection $connection */
$connection = $activityGuest->connections()
->where('type', Connection::TYPE_PSTN)
->firstOrFail();
$connection
->setTelephonyProviderId($data->getCallSid())
->setOpenedAt($dateTime)
->save();
event(new Events\Activities\Connections\Opened($connection));
return $this->joinTwilioConference($connection, [
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]);
}
private function answerOutboundCall(VO\ConferenceManager\SoftPhoneOutboundAnswer $data): TwiML\VoiceResponse
{
$connection = $data->getConnection();
$participant = $connection->getParticipant();
$activity = $data->getActivity();
$organiser = $activity->organizer;
$dateTime = now();
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
'connection' => $connection->id_string,
'participant' => $participant->id_string,
'activity' => $activity->id_string,
]);
if (! $participant->hasEnterTime()) {
$participant->update([
'enter_time' => $dateTime,
]);
}
$connection->update([
'is_muted' => $participant->isCoach(),
'opened_at' => $dateTime,
]);
event(new Events\Activities\Connections\Opened($connection));
if ($participant->isCoach()) {
/** @var Connection $roomOwnerConnection */
$roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();
return $this->joinTwilioConference(
$connection,
[
'coach' => $roomOwnerConnection->getTelephonyProviderId(),
]
);
}
return $this->joinTwilioConference(
$connection,
[
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]
);
}
private function determineUserCallerId(Models\User $user): string
{
if ($user->hasCallerId()) {
return $user->getCallerId();
}
// TODO: We should require a caller id to be verified before using the dialer
return $user->getSoftPhoneNumber() ?? $user->phone;
}
private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool
{
$startsWith = '[HTTP 404] Unable to create record: The requested resource';
$endsWith = 'was not found';
return Str::startsWith($exceptionMessage, $startsWith)
&& Str::endsWith($exceptionMessage, $endsWith);
}
/**
* @throws RestException
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOtherParty(Connection $callerConnection): self
{
$session = $callerConnection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$this->logger->debug(__METHOD__, [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {
throw new UnexpectedValueException('Unexpected activity type');
}
$callee = $activity->to;
$calleeId = $callee->getPhoneNumber();
/** @var Connection $calleeConnection */
$calleeConnection = $callee->connections()
->whereIn('type', [
Connection::TYPE_PSTN,
Connection::TYPE_WEBRTC,
])
->firstOrFail();
$caller = $activity->from;
// we need to take the number from the connection
// as the one we store on participant level might be the one we call to the rep via PSTN mode
$callerId = $callerConnection->getPhoneNumber();
if (! $caller->is($callerConnection->getParticipant())) {
throw new LogicException(sprintf(
'Unexpected caller participant. Expected: "%s", Actual: "%s"',
$caller->id_string,
$callerConnection->getParticipant()->id_string
));
}
$twilioConferenceSettings = [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
];
$hasInboundRecording = $activity->isTypeSoftphoneInbound()
&& $roomOwner->checkSoftphoneInboundRecordPreference();
$hasOutboundRecording = $activity->isTypeSoftPhone()
&& $roomOwner->checkSoftphoneOutboundRecordPreference();
$isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;
$isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()
|| $activity->hasRecordingReasonComplianceRestricted();
if ($isRecordingEnabled && ! $isRecordingRestricted) {
$twilioConferenceSettings = array_merge($twilioConferenceSettings, [
'record' => true,
'recordingChannels' => 'dual',
'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),
'recordingStatusCallbackMethod' => 'POST',
'recordingStatusCallbackEvent' => 'in-progress completed absent',
]);
if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {
$twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()
? 'outbound'
: 'inbound';
}
}
try {
if ($activity->hasRecordingReasonCompliancePrompted()) {
$this->logger->info('[Softphone] Request compliance prompt via outbound call', [
'activity_uuid' => $activity->getUuid(),
'session' => $session->id_string,
'conference_sid' => $session->getTelephonyProviderId(),
]);
$callInstance = $twilio->createCall(
$callerId,
$calleeId,
$this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [
'session' => $session->id_string,
'connection' => $calleeConnection->id_string,
]),
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $callInstance->sid,
'parent_connection_id' => $callerConnection->getId(),
]);
} else {
$twilioParticipantConnection = $twilio->createParticipantInstance(
$session->getTelephonyProviderId(),
$callerId,
$calleeId,
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $twilioParticipantConnection->callSid,
]);
}
} catch (RestException $restException) {
if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {
throw $restException;
}
if (! $activity->isInFiniteState()) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
$message = __METHOD__ . ': activity.status -> cancelled';
} else {
$message = sprintf(
__METHOD__ . ': not setting to cancelled, activity.status is %s',
$activity->getStatus()
);
}
$this->logger->info($message, [
'activity' => $activity->id_string,
'exception' => $restException,
]);
}
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
return $this;
}
private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array
{
$session = $connection->getSession();
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => false,
'record' => false,
'waitUrl' => '',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]),
'statusCallbackEvent' => [
'initiated',
'ringing',
'answered',
'completed',
],
'statusCallbackMethod' => 'POST',
'conferenceStatusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
]),
'conferenceStatusCallbackMethod' => 'POST',
'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function applyDefaultTwilioConferenceSettings(Models\Session $session, array $override = []): array
{
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => false,
'endConferenceOnExit' => false,
'waitUrl' => '',
'statusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
// 'speaker',
]),
'statusCallbackMethod' => 'POST',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function joinTwilioConference(Connection $connection, array $options = []): TwiML\VoiceResponse
{
$session = $connection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$this->logger->debug(__METHOD__, [
'connection' => $connection->id_string,
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
]);
$voiceResponse = new TwiML\VoiceResponse();
$voiceResponse->dial()
->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]))
->conference(
$session->getIdString(),
$this->applyDefaultTwilioConferenceSettings($session, $options)
);
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'result' => $voiceResponse,
]);
return $voiceResponse;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny_jupiter
Sync Changes
Hide This Notification
Code changed:
Hide
19
19
17
Previous Highlighted Error
Next Highlighted Error
SELECT * FROM teams WHERE id = 1;
select * from crm_layouts where crm_configuration_id = 39;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 283;
SELECT * FROM crm_fields WHERE id = 2234;
SELECT * FROM crm_field_values WHERE crm_field_id = 2234;
select * from crm_profiles where user_id = 143;
select * from record_types where crm_configuration_id = 39; # 0121K000001MHElQAO,0121K000001MHEqQAO
select * from business_processes where crm_configuration_id = 39;
# 01941000000H669AAC, 01941000000H66JAAS
select * from record_type_field_values
where record_type_id IN (24);
select * from crm_field_values where id IN (2730);
select * from crm_configurations where id = 39;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce'; #1035
select * from users where team_id = 1; # 222 group 3
SELECT * FROM activities WHERE user_id = 222 order by id desc;
select * from sidekick_settings where team_id = 1;
select * from teams where id = 1;
select * from team_features where team_id = 1;
select * from activities where crm_configuration_id = 2
and provider = 'ms-teams' and id = 608765;
SELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id = '59523413338';
select * from sidekick_settings where team_id = 2;
SELECT * FROM activities WHERE id = 608660;
select * from activity_summary_logs where activity_id = 608660;
select * from ai_prompts where transcription_id = 11214;
# [PASSWORD_DOTS]
SELECT * FROM activities WHERE uuid_to_bin('ed78a437-2804-450e-ab2f-56ab1c641346') = uuid;
# id: 608818, crm: 59628809737
SELECT * FROM activities WHERE uuid_to_bin('36b06e55-afdd-4782-8dee-c624cd0af191') = uuid;
# id: 608821, crm: 59632069252
SELECT ce.start_time, ce.end_time, a.id, a.uuid, crm_provider_id, calendar_event_id, title,
playbook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id,
scheduled_start_time, scheduled_end_time, actual_start_time, actual_end_time, a.created_at
FROM activities a
join calendar_events ce on a.calendar_event_id = ce.id
WHERE a.id IN (608818, 608821);
select * from users where team_id = 1;
select * from team_settings where team_id = 1;
select * from crm_profiles where crm_configuration_id = 39 order by user_id;
select * from team_features where team_id = 1;
select * from users where team_id = 2;
SELECT * FROM activities WHERE uuid_to_bin('ec7647e9-5225-458b-b475-f31aa2769204') = uuid; # 612639
# Preslava N. Ivanova, grou id 3
SELECT * FROM opportunities WHERE uuid_to_bin('a2928fe5-aec5-46cb-85d9-7654c89e46a6') = uuid;
select * from activities where opportunity_id = 344 and actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00';
select
a.id,
a.type,
a.scheduled_start_time,
a.actual_start_time,
a.created_at,
a.opportunity_id,
a.status
FROM activities a
WHERE opportunity_id = 344
and status IN ('completed', 'received', 'delivered')
and (
(a.actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')
OR (a.created_at between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')
OR (a.scheduled_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00'))
;
SELECT * FROM users WHERE id = 222;
SELECT * FROM crm_profiles WHERE user_id = 222;
select * from crm_layouts where crm_configuration_id = 39;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 281;
select * from group_deal_risk_types;
select * from opportunities where team_id = 1;
SELECT * FROM opportunities WHERE id = 315;
SELECT * FROM crm_field_data WHERE object_id = 315;
select * from crm_field_data where object_id = 260;
select * from generic_ai_prompts where subject_id = 315;
select * from teams; # 36, 21, 121, [EMAIL]
SELECT * FROM social_accounts WHERE sociable_id = 121 and provider = 'bullhorn';
# [PASSWORD_DOTS]
select * from teams where id = 1;
select * from crm_configurations where id = 39;
select * from users where team_id = 1;
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 1;
# 1 - 00541000004281rAAA
# 204 - 0052g000003freeAAA
# 429 - 0052g000003qGOiAAM
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce';
select * from activities where type = 'softphone'
and created_at > '2024-12-11 15:24:36' order by id desc;
select * from activity_providers where team_id = 1;
select * from activity_provider_users where activity_provider_id = 328;
select * from opportunities where crm_configuration_id = 39
AND account_id = 178 AND is_closed = false
order by created_at DESC;
select * from contacts where id = 3952;
select * from accounts where id = 178;
# [PASSWORD_DOTS]
select * from teams where id = 36;
select * from crm_configurations where id = 21;
select * from users where team_id = 36;
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 36;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 36
and sa.provider = 'bullhorn';
select * from social_accounts where id = 348;
UPDATE social_accounts SET
provider_user_token = '21442_6802599_91:41179a58-21e7-4d7c-ad58-56bb666b2f65',
provider_refresh_token = '21442_6802599_91:01c6b335-3f2a-42e4-85ff-8a08fa65fceb',
expires = 1733998131,
state = 'connected'
WHERE id = 348;
# [PASSWORD_DOTS]
select * from teams where id = 31;
select * from crm_configurations where id = 18;
select * from users where team_id = 31; # 257
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 31;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 31
and sa.provider = 'close';
select * from contacts where crm_configuration_id = 18;
# [PASSWORD_DOTS] NEPTUNE [PASSWORD_DOTS]
select * from teams;
select * from users where id IN (1030, 1035, 1052);
select * from crm_configurations;
select * from users where team_id = 65; # 257
select * from team_settings where team_id = 65; # 257
select * from invitations where team_id = 65; # 257
select * from users where email = '[EMAIL]'; # 257
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 65;
select * from crm_configurations where id = 53;
select * from accounts where crm_configuration_id = 53 order by id desc;
select * from leads where crm_configuration_id = 53 order by id desc;
select * from contacts where crm_configuration_id = 53 order by id desc;
select * from opportunities where crm_configuration_id = 53 order by id desc;
select * from crm_profiles where crm_configuration_id = 53 order by id desc;
select * from crm_fields where crm_configuration_id = 53 order by id desc;
select * from crm_field_values where crm_field_id = 3341 order by id desc;
select * from crm_layouts where crm_configuration_id = 53 order by id desc;
select * from stages where crm_configuration_id = 53 order by id desc;
select * from crm_profiles where crm_configuration_id = 13;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 65
and sa.provider = 'integration-app';
select * from contacts where crm_configuration_id = 13;
select * from social_accounts where sociable_id = 283;
SELECT * FROM opportunities WHERE crm_provider_id = '006O400000E9bzeIAB';
select * from activity_providers where team_id = 65;
SELECT * FROM activities WHERE crm_configuration_id IN (51, 52, 53);
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 65
;
# [PASSWORD_DOTS] STAGING [PASSWORD_DOTS]
SELECT * FROM teams;
SELECT * FROM teams WHERE id = 88;
SELECT * FROM teams WHERE id = 89;
select * from team_settings where team_id = 89;
SELECT * FROM users WHERE team_id = 89;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 89;
select * from users;
SELECT * FROM social_accounts WHERE sociable_id = 1761;
SELECT * FROM crm_configurations WHERE id = 70;
select * from accounts where crm_configuration_id = 70 order by id desc;
select * from leads where crm_configuration_id = 70 order by id desc;
select * from contacts where crm_configuration_id = 70 order by id desc;
select * from opportunities where crm_configuration_id = 70 order by id desc;
select * from crm_profiles where crm_configuration_id = 70 order by id desc;
select * from crm_fields where crm_configuration_id = 70 order by id desc;
select * from crm_field_values where crm_field_id = 3536 order by id desc;
select * from crm_layouts where crm_configuration_id = 70 order by id desc;
select * from stages where crm_configuration_id = 70 order by id desc;
select * from business_processes where crm_configuration_id = 70 order by id desc;
select * from business_process_stages where business_process_id = 34;
select * from contacts where id = 10468;
select * from crm_layouts where crm_configuration_id = 70;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 388;
SELECT * FROM crm_fields WHERE id IN (3533,3534,3535);
select * from activities where crm_configuration_id = 70
and (account_id IS NOT NULL or lead_id IS NOT NULL or contact_id IS NOT NULL or opportunity_id IS NOT NULL) order by id desc;
SELECT * FROM activities WHERE uuid_to_bin('2e10b60f-8a61-41c5-a3d4-28835353dc65') = uuid;
SELECT * FROM activities where crm_configuration_id = 69 ;
SELECT * FROM users WHERE email LIKE '%[EMAIL]%';
SELECT * FROM activities WHERE uuid_to_bin('5a150c93-40fc-42ec-b3bd-c1d328e09f6e') = uuid;
SELECT * FROM opportunities WHERE id = 385;
select * from participants p
join activities a on p.activity_id = a.id
where a.crm_configuration_id = 70
and (p.lead_id IS NOT NULL or p.contact_id IS NOT NULL);
SELECT * FROM participants WHERE id = 1013638;
select * from teams where id = 90;
select * from users where team_id = 90;
select * from social_accounts where social_accounts.sociable_id IN (1960,1760);
SELECT * FROM crm_profiles WHERE crm_configuration_id = 71;
select * from invitations where team_id = 90;
select * from crm_configurations where id = 71;
select * from accounts where crm_configuration_id = 71 order by id desc;
select * from leads where crm_configuration_id = 71 order by id desc;
select * from contacts where crm_configuration_id = 71 order by id desc;
select * from opportunities where crm_configuration_id = 71 order by id desc;
select * from crm_profiles where crm_configuration_id = 71 order by id desc;
select * from crm_fields where crm_configuration_id = 71 order by id desc;
select * from crm_field_values where crm_field_id = 3341 order by id desc;
select * from crm_layouts where crm_configuration_id = 71 order by id desc;
select * from stages where crm_configuration_id = 71 order by id desc;
select * from users order by secondary_email desc;
select u.id, u.email, u.status, sa.id, sa.provider_user_id from social_accounts sa
join users u on sa.sociable_id = u.id
where sa.provider = 'google' and u.email LIKE 'aneliya%';
select * from failed_jobs order by id desc;
select * from users where email = '[EMAIL]' or secondary_email = '[EMAIL]';
select * from teams;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 39;
SELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type = 'task';
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce';
# [PASSWORD_DOTS]
SELECT * FROM activities WHERE uuid_to_bin('c38b3895-fd0f-4b1f-9fb2-c170dba137c6') = uuid;
SELECT * FROM crm_configurations WHERE id = 70;
select * from teams where id = 1;
select * from groups where team_id = 1;
select * from users where team_id = 1;
select o.id, o.name,o.close_date, u.id, u.name, u.group_id, r.id, r.display_name, g.name, g.scope from opportunities o
join users u on o.user_id = u.id
join groups g on u.group_id = g.id
join...
|
[{"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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","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":"9","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","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\\Component\\Twilio\\Conference\\ConferenceManager;\n\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Support\\Str;\nuse Jiminny\\Component\\ElasticSearch\\Model\\Observer;\nuse Jiminny\\Component\\Twilio\\Conference\\ConferenceManager;\nuse Jiminny\\Component\\Twilio\\Exception\\OutOfBoundsException;\nuse Jiminny\\Component\\Twilio\\Exception\\UnexpectedValueException;\nuse Jiminny\\Component\\Twilio\\Resolver;\nuse Jiminny\\Component\\Twilio\\VO;\nuse Jiminny\\Component\\UrlGenerator\\Webhook;\nuse Jiminny\\Events;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Services\\ActivityService;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Crm\\CachedCrmServiceDecorator;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Services\\Telephony\\TwilioClientBuilder;\nuse Psr\\Log\\LoggerInterface;\nuse Twilio\\Exceptions\\ConfigurationException;\nuse Twilio\\Exceptions\\RestException;\nuse Twilio\\Exceptions\\TwilioException;\nuse Twilio\\TwiML;\n\nfinal readonly class SoftPhoneManager implements ConferenceManager\n{\n public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;\n public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';\n\n public function __construct(\n private ProviderRegistry $crmProviderRegistry,\n private Webhook $urlGenerator,\n private ActivityService $activityService,\n private LoggerInterface $logger,\n private TwilioClientBuilder $twilioClientBuilder,\n ) {\n }\n\n public function create(VO\\ConferenceManager\\ActivityCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n 'instanceof' => get_class($data),\n ]);\n\n $activity = Observer::skipEvents(function () use ($data) {\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundCreate) {\n $activity = $this->createInboundActivity($data);\n } elseif ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundCreate) {\n $activity = $this->createOutboundActivity($data);\n } else {\n throw new LogicException(sprintf(\n 'Unknown softphone implementation: %s',\n get_class($data)\n ));\n }\n\n return $activity;\n });\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'instanceof' => get_class($data),\n 'activity' => $activity->id_string,\n ]);\n\n return $activity;\n }\n\n private function createOutboundActivity(VO\\ConferenceManager\\SoftPhoneOutboundCreate $data): Models\\Activity\n {\n $startTime = microtime(true);\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n $now = now();\n $user = $data->getUser();\n $team = $user->getTeam();\n $device = $data->getDevice();\n $phoneNumber = $data->getPhoneNumber();\n $route = $data->getPhoneRoute();\n\n if (! $this->activityService->teamCanDial($team, $phoneNumber)) {\n $region = getRegionByNumber($phoneNumber, $user->getCountryCode());\n\n throw new LogicException(sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? 'this number'\n ));\n }\n\n $lead = null;\n $account = null;\n $contact = null;\n $stage = null;\n $opportunity = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $user,\n 'providerSlug' => $team->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n } catch (\\Throwable $throwable) {\n $this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [\n 'exception' => $throwable,\n 'user_id' => $user->getId(),\n 'team_id' => $team->getId(),\n ]);\n $crmService = $this->crmProviderRegistry->get($team->crm->provider);\n if (! $user->isCrmRequired()) {\n $crmService->setUser($user->team->owner);\n } else {\n $crmService->setUser($user);\n }\n }\n\n $prospectId = $data->getProspectId();\n\n try {\n if ($prospectId !== null) {\n $crmStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [\n 'prospect_id' => $prospectId,\n 'object_type' => $data->getObjectType(),\n ]);\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(\n $prospectId,\n $data->getObjectType()\n );\n $this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),\n 'found_lead' => $lead !== null,\n 'found_account' => $account !== null,\n 'found_opportunity' => $opportunity !== null,\n 'found_contact' => $contact !== null,\n ]);\n\n // Try to attach a contact if we have an opportunity.\n if ($opportunity !== null && $contact === null) {\n $matchStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');\n [, , , $contact, ] = $crmService->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n $this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),\n 'found_contact' => $contact !== null,\n ]);\n }\n } else {\n $decorator = resolve(CachedCrmServiceDecorator::class);\n $decorator->setCrmService($crmService);\n\n [$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n }\n } catch (\\Throwable $throwable) {\n $this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.\n }\n\n if ($data->hasProspectStage()) {\n /** @var Models\\Stage $stage */\n $stage = $team->crm->stages()->uuid($data->getProspectStage());\n }\n\n $dbStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting activity creation in DB');\n $activity = $user->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'device_id' => $device->id ?? null,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'value' => $opportunity ? $opportunity->value : null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_PENDING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),\n 'scheduled_start_time' => $now,\n ]);\n $this->logger->info('[SoftPhoneManager] Activity created in DB', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),\n 'activity_id' => $activity->id ?? null,\n ]);\n\n if (! $activity instanceof Models\\Activity) {\n throw new LogicException('Activity model expected');\n }\n\n $regionId = null;\n if ($user->hasRegionId()) {\n $regionId = $user->getRegionId();\n } elseif ($data->hasRegion()) {\n $regionId = $data->getRegion()->getId();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $regionId,\n 'is_muted' => false,\n ]);\n\n $phoneRouteResolver = new Resolver\\PhoneRouteResolver($user, $route);\n $phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();\n $connectionType = $phoneRouteResolver->getConnectionType();\n $callerId = $this->determineUserCallerId($user);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'user_id' => $user->id,\n 'email' => $user->email,\n 'name' => $user->name,\n 'phone_number' => $phoneRouteResolver->mustUsePSTN()\n ? $phoneNumberOrigin\n : $callerId,\n ]);\n\n $caller->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',\n 'phone' => $callerId,\n 'type' => $connectionType,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $phoneNumber,\n 'name' => $activity->prospect_name,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'phone' => $phoneNumber,\n 'direction' => 'outbound-api',\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $activity->refresh();\n\n $this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [\n 'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'activity_id' => $activity->id_string,\n ]);\n $this->logger->debug(__METHOD__ . ': done', [\n 'data' => $data,\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n return $activity;\n }\n\n private function createInboundActivity(VO\\ConferenceManager\\SoftPhoneInboundCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $roomOwner = $data->getUser();\n $team = $roomOwner->getTeam();\n $phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();\n $callerId = $data->getCallerId();\n $callerCallSid = $data->getCallSid();\n\n $now = now();\n\n if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {\n $region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());\n\n throw new LogicException(\n sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? ' this number',\n ),\n );\n }\n\n $lead = $account = $opportunity = $contact = $stage = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $roomOwner->getTeam(),\n 'integrationAdmin' => $roomOwner,\n 'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(\n $callerId,\n null,\n $roomOwner->getId()\n );\n } catch (\\Throwable $throwable) {\n $this->logger->error(__METHOD__ . ': crm exception', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening\n }\n\n /** @var Models\\Activity $activity */\n $activity = $roomOwner->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE_INBOUND,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_RINGING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),\n 'scheduled_start_time' => $now,\n ]);\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $roomOwner->getRegionId(),\n 'is_muted' => false,\n ]);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $callerId,\n 'name' => $activity->prospect_name,\n 'enter_time' => $now,\n ]);\n\n $caller->connections()->create([\n 'telephony_provider_id' => $callerCallSid,\n 'session_id' => $session->getId(),\n 'phone' => $callerId,\n 'direction' => 'inbound',\n 'type' => Connection::TYPE_PSTN,\n 'opened_at' => $now,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'user_id' => $roomOwner->id,\n 'email' => $roomOwner->email,\n 'name' => $roomOwner->name,\n 'phone_number' => $phoneNumber,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => 'outbound-api',\n 'phone' => $phoneNumber,\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'callSid' => $data->getCallSid(),\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n $activity->refresh();\n\n return $activity;\n }\n\n private function getVoiceRecordingReasonCode(Models\\Team $team, string $phoneNumber): int\n {\n $this->logger->debug(__METHOD__, [\n 'team' => $team->id_string,\n 'phone' => $phoneNumber,\n ]);\n\n if (\n ! $team->hasComplianceModeRecordingPrompt()\n && ! $team->hasComplianceModeRecordingRestrictToOneSide()\n && ! $team->hasComplianceModeRecordingRestrictRecording()\n ) {\n return 0;\n }\n\n /** @var Models\\VoiceConsentPrefix|null $voiceConsentPolicy */\n $voiceConsentPolicy = Models\\VoiceConsentPrefix::query()\n ->whereRaw(\"? LIKE CONCAT(destination_prefix, '%')\", [$phoneNumber])\n ->where(static function (Eloquent\\Builder $builder) use ($team): void {\n $builder\n ->whereNull('team_id')\n ->orWhere('team_id', $team->getId());\n })\n ->orderByDesc('team_id')\n ->first();\n\n if (! $voiceConsentPolicy instanceof Models\\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {\n return 0;\n }\n\n if ($team->hasComplianceModeRecordingPrompt()) {\n // This number requires consent from the other party before recording the call.\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;\n }\n\n if ($team->hasComplianceModeRecordingRestrictToOneSide()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;\n }\n\n if ($team->hasComplianceModeRecordingRestrictRecording()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;\n }\n\n return 0;\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOrganiser(Models\\Activity $activity): void\n {\n $this->logger->debug(__METHOD__, [\n 'activity' => $activity->id_string,\n ]);\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $canBeDialed =\n $activity->isTypeSoftPhone()\n && ! $activity->hasStarted()\n && $activity->isPending()\n && $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->exists();\n\n if (! $canBeDialed) {\n throw new OutOfBoundsException('Cannot make an outbound call to this activity.');\n }\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->firstOrFail();\n\n /** @var Models\\Session $session */\n $session = $organiserConnection->getSession();\n\n $twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ]);\n\n // What number will the organizer see as caller id\n $callerId = $roomOwner->getSoftPhoneNumber();\n\n if ($callerId === null) {\n $callee = $activity->participants()->whereNull('user_id')->first();\n // This is a bridge call. We make it look like the callee is calling the organizer\n $callerId = $callee->phone_number;\n }\n\n // What number will the prospect see as caller id\n $calleeId = $organiser->getPhoneNumber();\n\n $this->logger->debug(__METHOD__ . ': twilio call', [\n 'calleeId' => $calleeId,\n 'callerId' => $callerId,\n ]);\n\n $twilioParticipant = $twilio->createParticipantInstance(\n $session->id_string,\n $callerId,\n $calleeId,\n $twilioConferenceSettings\n );\n\n $organiserConnection\n ->setTelephonyProviderId($twilioParticipant->callSid)\n ->save();\n\n $session\n ->setTelephonyProviderId($twilioParticipant->conferenceSid)\n ->save();\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function dialCoach(Models\\Activity $activity, Models\\User $user): void\n {\n throw new \\LogicException('Not implemented yet');\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function terminate(VO\\ConferenceManager\\ActivityTerminate $data): void\n {\n if (! $data instanceof VO\\ConferenceManager\\SoftphoneTerminate) {\n throw new UnexpectedValueException(sprintf(\n 'Expecting an instance of \"%s\", but \"%s\" given.',\n VO\\ConferenceManager\\SoftphoneTerminate::class,\n \\get_class($data)\n ));\n }\n\n $this->logger->debug(__METHOD__, [\n 'activity' => $data->getActivity()->id_string,\n 'user' => $data->getUser()->id_string,\n ]);\n\n $activity = $data->getActivity();\n $roomOwner = $activity->getUser();\n\n $isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();\n\n if ($isCancelling) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions->first();\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->whereIn('type', [\n Connection::TYPE_WEBRTC,\n Connection::TYPE_PSTN,\n ])\n ->firstOrFail();\n\n if (! $organiserConnection->hasTelephonyProviderId()) {\n throw new LogicException(\n 'Unable to terminate call as we do not yet know the organiser telephony provider id',\n );\n }\n\n $twilioClient = $this->twilioClientBuilder->build($activity->getTeam());\n\n $twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());\n\n $session->hasTelephonyProviderId()\n && $twilioClient->terminateConference($session->getTelephonyProviderId());\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function answer(VO\\ConferenceManager\\ActivityAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundAnswer) {\n return $this->answerInboundCall($data);\n }\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundAnswer) {\n return $this->answerOutboundCall($data);\n }\n\n throw new LogicException('Unable to answer - not supported activity answer instance');\n }\n\n private function answerInboundCall(VO\\ConferenceManager\\SoftPhoneInboundAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $dateTime = now();\n\n $activity = $this->create(\n new VO\\ConferenceManager\\SoftPhoneInboundCreate(\n $data->getCallerId(),\n $data->getCallSid(),\n $data->getUser()\n )\n );\n\n /** @var Models\\Participant $activityGuest */\n $activityGuest = $activity->from;\n\n $activityGuest\n ->setEnterTime($dateTime)\n ->save();\n\n /** @var Connection $connection */\n $connection = $activityGuest->connections()\n ->where('type', Connection::TYPE_PSTN)\n ->firstOrFail();\n\n $connection\n ->setTelephonyProviderId($data->getCallSid())\n ->setOpenedAt($dateTime)\n ->save();\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n return $this->joinTwilioConference($connection, [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]);\n }\n\n private function answerOutboundCall(VO\\ConferenceManager\\SoftPhoneOutboundAnswer $data): TwiML\\VoiceResponse\n {\n $connection = $data->getConnection();\n $participant = $connection->getParticipant();\n $activity = $data->getActivity();\n $organiser = $activity->organizer;\n $dateTime = now();\n\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n 'connection' => $connection->id_string,\n 'participant' => $participant->id_string,\n 'activity' => $activity->id_string,\n ]);\n\n if (! $participant->hasEnterTime()) {\n $participant->update([\n 'enter_time' => $dateTime,\n ]);\n }\n\n $connection->update([\n 'is_muted' => $participant->isCoach(),\n 'opened_at' => $dateTime,\n ]);\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n if ($participant->isCoach()) {\n /** @var Connection $roomOwnerConnection */\n $roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'coach' => $roomOwnerConnection->getTelephonyProviderId(),\n ]\n );\n }\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]\n );\n }\n\n private function determineUserCallerId(Models\\User $user): string\n {\n if ($user->hasCallerId()) {\n return $user->getCallerId();\n }\n\n // TODO: We should require a caller id to be verified before using the dialer\n return $user->getSoftPhoneNumber() ?? $user->phone;\n }\n\n private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool\n {\n $startsWith = '[HTTP 404] Unable to create record: The requested resource';\n $endsWith = 'was not found';\n\n return Str::startsWith($exceptionMessage, $startsWith)\n && Str::endsWith($exceptionMessage, $endsWith);\n }\n\n /**\n * @throws RestException\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOtherParty(Connection $callerConnection): self\n {\n $session = $callerConnection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $this->logger->debug(__METHOD__, [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {\n throw new UnexpectedValueException('Unexpected activity type');\n }\n\n $callee = $activity->to;\n $calleeId = $callee->getPhoneNumber();\n\n /** @var Connection $calleeConnection */\n $calleeConnection = $callee->connections()\n ->whereIn('type', [\n Connection::TYPE_PSTN,\n Connection::TYPE_WEBRTC,\n ])\n ->firstOrFail();\n\n $caller = $activity->from;\n\n // we need to take the number from the connection\n // as the one we store on participant level might be the one we call to the rep via PSTN mode\n $callerId = $callerConnection->getPhoneNumber();\n\n if (! $caller->is($callerConnection->getParticipant())) {\n throw new LogicException(sprintf(\n 'Unexpected caller participant. Expected: \"%s\", Actual: \"%s\"',\n $caller->id_string,\n $callerConnection->getParticipant()->id_string\n ));\n }\n\n $twilioConferenceSettings = [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ];\n\n $hasInboundRecording = $activity->isTypeSoftphoneInbound()\n && $roomOwner->checkSoftphoneInboundRecordPreference();\n\n $hasOutboundRecording = $activity->isTypeSoftPhone()\n && $roomOwner->checkSoftphoneOutboundRecordPreference();\n\n $isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;\n\n $isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()\n || $activity->hasRecordingReasonComplianceRestricted();\n\n if ($isRecordingEnabled && ! $isRecordingRestricted) {\n $twilioConferenceSettings = array_merge($twilioConferenceSettings, [\n 'record' => true,\n 'recordingChannels' => 'dual',\n 'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),\n 'recordingStatusCallbackMethod' => 'POST',\n 'recordingStatusCallbackEvent' => 'in-progress completed absent',\n ]);\n\n if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n $twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()\n ? 'outbound'\n : 'inbound';\n }\n }\n\n try {\n if ($activity->hasRecordingReasonCompliancePrompted()) {\n $this->logger->info('[Softphone] Request compliance prompt via outbound call', [\n 'activity_uuid' => $activity->getUuid(),\n 'session' => $session->id_string,\n 'conference_sid' => $session->getTelephonyProviderId(),\n ]);\n\n $callInstance = $twilio->createCall(\n $callerId,\n $calleeId,\n $this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [\n 'session' => $session->id_string,\n 'connection' => $calleeConnection->id_string,\n ]),\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $callInstance->sid,\n 'parent_connection_id' => $callerConnection->getId(),\n ]);\n } else {\n $twilioParticipantConnection = $twilio->createParticipantInstance(\n $session->getTelephonyProviderId(),\n $callerId,\n $calleeId,\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $twilioParticipantConnection->callSid,\n ]);\n }\n } catch (RestException $restException) {\n if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {\n throw $restException;\n }\n\n if (! $activity->isInFiniteState()) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n\n $message = __METHOD__ . ': activity.status -> cancelled';\n } else {\n $message = sprintf(\n __METHOD__ . ': not setting to cancelled, activity.status is %s',\n $activity->getStatus()\n );\n }\n\n $this->logger->info($message, [\n 'activity' => $activity->id_string,\n 'exception' => $restException,\n ]);\n }\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n return $this;\n }\n\n private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array\n {\n $session = $connection->getSession();\n\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => false,\n 'record' => false,\n 'waitUrl' => '',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]),\n 'statusCallbackEvent' => [\n 'initiated',\n 'ringing',\n 'answered',\n 'completed',\n ],\n 'statusCallbackMethod' => 'POST',\n 'conferenceStatusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n ]),\n 'conferenceStatusCallbackMethod' => 'POST',\n 'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function applyDefaultTwilioConferenceSettings(Models\\Session $session, array $override = []): array\n {\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => false,\n 'endConferenceOnExit' => false,\n 'waitUrl' => '',\n 'statusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n// 'speaker',\n ]),\n 'statusCallbackMethod' => 'POST',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function joinTwilioConference(Connection $connection, array $options = []): TwiML\\VoiceResponse\n {\n $session = $connection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n\n $this->logger->debug(__METHOD__, [\n 'connection' => $connection->id_string,\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n ]);\n\n $voiceResponse = new TwiML\\VoiceResponse();\n\n $voiceResponse->dial()\n ->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]))\n ->conference(\n $session->getIdString(),\n $this->applyDefaultTwilioConferenceSettings($session, $options)\n );\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'result' => $voiceResponse,\n ]);\n\n return $voiceResponse;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Twilio\\Conference\\ConferenceManager;\n\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Support\\Str;\nuse Jiminny\\Component\\ElasticSearch\\Model\\Observer;\nuse Jiminny\\Component\\Twilio\\Conference\\ConferenceManager;\nuse Jiminny\\Component\\Twilio\\Exception\\OutOfBoundsException;\nuse Jiminny\\Component\\Twilio\\Exception\\UnexpectedValueException;\nuse Jiminny\\Component\\Twilio\\Resolver;\nuse Jiminny\\Component\\Twilio\\VO;\nuse Jiminny\\Component\\UrlGenerator\\Webhook;\nuse Jiminny\\Events;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Services\\ActivityService;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Crm\\CachedCrmServiceDecorator;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Services\\Telephony\\TwilioClientBuilder;\nuse Psr\\Log\\LoggerInterface;\nuse Twilio\\Exceptions\\ConfigurationException;\nuse Twilio\\Exceptions\\RestException;\nuse Twilio\\Exceptions\\TwilioException;\nuse Twilio\\TwiML;\n\nfinal readonly class SoftPhoneManager implements ConferenceManager\n{\n public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;\n public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';\n\n public function __construct(\n private ProviderRegistry $crmProviderRegistry,\n private Webhook $urlGenerator,\n private ActivityService $activityService,\n private LoggerInterface $logger,\n private TwilioClientBuilder $twilioClientBuilder,\n ) {\n }\n\n public function create(VO\\ConferenceManager\\ActivityCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n 'instanceof' => get_class($data),\n ]);\n\n $activity = Observer::skipEvents(function () use ($data) {\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundCreate) {\n $activity = $this->createInboundActivity($data);\n } elseif ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundCreate) {\n $activity = $this->createOutboundActivity($data);\n } else {\n throw new LogicException(sprintf(\n 'Unknown softphone implementation: %s',\n get_class($data)\n ));\n }\n\n return $activity;\n });\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'instanceof' => get_class($data),\n 'activity' => $activity->id_string,\n ]);\n\n return $activity;\n }\n\n private function createOutboundActivity(VO\\ConferenceManager\\SoftPhoneOutboundCreate $data): Models\\Activity\n {\n $startTime = microtime(true);\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n $now = now();\n $user = $data->getUser();\n $team = $user->getTeam();\n $device = $data->getDevice();\n $phoneNumber = $data->getPhoneNumber();\n $route = $data->getPhoneRoute();\n\n if (! $this->activityService->teamCanDial($team, $phoneNumber)) {\n $region = getRegionByNumber($phoneNumber, $user->getCountryCode());\n\n throw new LogicException(sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? 'this number'\n ));\n }\n\n $lead = null;\n $account = null;\n $contact = null;\n $stage = null;\n $opportunity = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $user,\n 'providerSlug' => $team->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n } catch (\\Throwable $throwable) {\n $this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [\n 'exception' => $throwable,\n 'user_id' => $user->getId(),\n 'team_id' => $team->getId(),\n ]);\n $crmService = $this->crmProviderRegistry->get($team->crm->provider);\n if (! $user->isCrmRequired()) {\n $crmService->setUser($user->team->owner);\n } else {\n $crmService->setUser($user);\n }\n }\n\n $prospectId = $data->getProspectId();\n\n try {\n if ($prospectId !== null) {\n $crmStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [\n 'prospect_id' => $prospectId,\n 'object_type' => $data->getObjectType(),\n ]);\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(\n $prospectId,\n $data->getObjectType()\n );\n $this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),\n 'found_lead' => $lead !== null,\n 'found_account' => $account !== null,\n 'found_opportunity' => $opportunity !== null,\n 'found_contact' => $contact !== null,\n ]);\n\n // Try to attach a contact if we have an opportunity.\n if ($opportunity !== null && $contact === null) {\n $matchStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');\n [, , , $contact, ] = $crmService->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n $this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),\n 'found_contact' => $contact !== null,\n ]);\n }\n } else {\n $decorator = resolve(CachedCrmServiceDecorator::class);\n $decorator->setCrmService($crmService);\n\n [$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n }\n } catch (\\Throwable $throwable) {\n $this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.\n }\n\n if ($data->hasProspectStage()) {\n /** @var Models\\Stage $stage */\n $stage = $team->crm->stages()->uuid($data->getProspectStage());\n }\n\n $dbStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting activity creation in DB');\n $activity = $user->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'device_id' => $device->id ?? null,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'value' => $opportunity ? $opportunity->value : null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_PENDING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),\n 'scheduled_start_time' => $now,\n ]);\n $this->logger->info('[SoftPhoneManager] Activity created in DB', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),\n 'activity_id' => $activity->id ?? null,\n ]);\n\n if (! $activity instanceof Models\\Activity) {\n throw new LogicException('Activity model expected');\n }\n\n $regionId = null;\n if ($user->hasRegionId()) {\n $regionId = $user->getRegionId();\n } elseif ($data->hasRegion()) {\n $regionId = $data->getRegion()->getId();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $regionId,\n 'is_muted' => false,\n ]);\n\n $phoneRouteResolver = new Resolver\\PhoneRouteResolver($user, $route);\n $phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();\n $connectionType = $phoneRouteResolver->getConnectionType();\n $callerId = $this->determineUserCallerId($user);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'user_id' => $user->id,\n 'email' => $user->email,\n 'name' => $user->name,\n 'phone_number' => $phoneRouteResolver->mustUsePSTN()\n ? $phoneNumberOrigin\n : $callerId,\n ]);\n\n $caller->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',\n 'phone' => $callerId,\n 'type' => $connectionType,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $phoneNumber,\n 'name' => $activity->prospect_name,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'phone' => $phoneNumber,\n 'direction' => 'outbound-api',\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $activity->refresh();\n\n $this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [\n 'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'activity_id' => $activity->id_string,\n ]);\n $this->logger->debug(__METHOD__ . ': done', [\n 'data' => $data,\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n return $activity;\n }\n\n private function createInboundActivity(VO\\ConferenceManager\\SoftPhoneInboundCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $roomOwner = $data->getUser();\n $team = $roomOwner->getTeam();\n $phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();\n $callerId = $data->getCallerId();\n $callerCallSid = $data->getCallSid();\n\n $now = now();\n\n if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {\n $region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());\n\n throw new LogicException(\n sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? ' this number',\n ),\n );\n }\n\n $lead = $account = $opportunity = $contact = $stage = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $roomOwner->getTeam(),\n 'integrationAdmin' => $roomOwner,\n 'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(\n $callerId,\n null,\n $roomOwner->getId()\n );\n } catch (\\Throwable $throwable) {\n $this->logger->error(__METHOD__ . ': crm exception', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening\n }\n\n /** @var Models\\Activity $activity */\n $activity = $roomOwner->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE_INBOUND,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_RINGING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),\n 'scheduled_start_time' => $now,\n ]);\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $roomOwner->getRegionId(),\n 'is_muted' => false,\n ]);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $callerId,\n 'name' => $activity->prospect_name,\n 'enter_time' => $now,\n ]);\n\n $caller->connections()->create([\n 'telephony_provider_id' => $callerCallSid,\n 'session_id' => $session->getId(),\n 'phone' => $callerId,\n 'direction' => 'inbound',\n 'type' => Connection::TYPE_PSTN,\n 'opened_at' => $now,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'user_id' => $roomOwner->id,\n 'email' => $roomOwner->email,\n 'name' => $roomOwner->name,\n 'phone_number' => $phoneNumber,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => 'outbound-api',\n 'phone' => $phoneNumber,\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'callSid' => $data->getCallSid(),\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n $activity->refresh();\n\n return $activity;\n }\n\n private function getVoiceRecordingReasonCode(Models\\Team $team, string $phoneNumber): int\n {\n $this->logger->debug(__METHOD__, [\n 'team' => $team->id_string,\n 'phone' => $phoneNumber,\n ]);\n\n if (\n ! $team->hasComplianceModeRecordingPrompt()\n && ! $team->hasComplianceModeRecordingRestrictToOneSide()\n && ! $team->hasComplianceModeRecordingRestrictRecording()\n ) {\n return 0;\n }\n\n /** @var Models\\VoiceConsentPrefix|null $voiceConsentPolicy */\n $voiceConsentPolicy = Models\\VoiceConsentPrefix::query()\n ->whereRaw(\"? LIKE CONCAT(destination_prefix, '%')\", [$phoneNumber])\n ->where(static function (Eloquent\\Builder $builder) use ($team): void {\n $builder\n ->whereNull('team_id')\n ->orWhere('team_id', $team->getId());\n })\n ->orderByDesc('team_id')\n ->first();\n\n if (! $voiceConsentPolicy instanceof Models\\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {\n return 0;\n }\n\n if ($team->hasComplianceModeRecordingPrompt()) {\n // This number requires consent from the other party before recording the call.\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;\n }\n\n if ($team->hasComplianceModeRecordingRestrictToOneSide()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;\n }\n\n if ($team->hasComplianceModeRecordingRestrictRecording()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;\n }\n\n return 0;\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOrganiser(Models\\Activity $activity): void\n {\n $this->logger->debug(__METHOD__, [\n 'activity' => $activity->id_string,\n ]);\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $canBeDialed =\n $activity->isTypeSoftPhone()\n && ! $activity->hasStarted()\n && $activity->isPending()\n && $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->exists();\n\n if (! $canBeDialed) {\n throw new OutOfBoundsException('Cannot make an outbound call to this activity.');\n }\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->firstOrFail();\n\n /** @var Models\\Session $session */\n $session = $organiserConnection->getSession();\n\n $twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ]);\n\n // What number will the organizer see as caller id\n $callerId = $roomOwner->getSoftPhoneNumber();\n\n if ($callerId === null) {\n $callee = $activity->participants()->whereNull('user_id')->first();\n // This is a bridge call. We make it look like the callee is calling the organizer\n $callerId = $callee->phone_number;\n }\n\n // What number will the prospect see as caller id\n $calleeId = $organiser->getPhoneNumber();\n\n $this->logger->debug(__METHOD__ . ': twilio call', [\n 'calleeId' => $calleeId,\n 'callerId' => $callerId,\n ]);\n\n $twilioParticipant = $twilio->createParticipantInstance(\n $session->id_string,\n $callerId,\n $calleeId,\n $twilioConferenceSettings\n );\n\n $organiserConnection\n ->setTelephonyProviderId($twilioParticipant->callSid)\n ->save();\n\n $session\n ->setTelephonyProviderId($twilioParticipant->conferenceSid)\n ->save();\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function dialCoach(Models\\Activity $activity, Models\\User $user): void\n {\n throw new \\LogicException('Not implemented yet');\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function terminate(VO\\ConferenceManager\\ActivityTerminate $data): void\n {\n if (! $data instanceof VO\\ConferenceManager\\SoftphoneTerminate) {\n throw new UnexpectedValueException(sprintf(\n 'Expecting an instance of \"%s\", but \"%s\" given.',\n VO\\ConferenceManager\\SoftphoneTerminate::class,\n \\get_class($data)\n ));\n }\n\n $this->logger->debug(__METHOD__, [\n 'activity' => $data->getActivity()->id_string,\n 'user' => $data->getUser()->id_string,\n ]);\n\n $activity = $data->getActivity();\n $roomOwner = $activity->getUser();\n\n $isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();\n\n if ($isCancelling) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions->first();\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->whereIn('type', [\n Connection::TYPE_WEBRTC,\n Connection::TYPE_PSTN,\n ])\n ->firstOrFail();\n\n if (! $organiserConnection->hasTelephonyProviderId()) {\n throw new LogicException(\n 'Unable to terminate call as we do not yet know the organiser telephony provider id',\n );\n }\n\n $twilioClient = $this->twilioClientBuilder->build($activity->getTeam());\n\n $twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());\n\n $session->hasTelephonyProviderId()\n && $twilioClient->terminateConference($session->getTelephonyProviderId());\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function answer(VO\\ConferenceManager\\ActivityAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundAnswer) {\n return $this->answerInboundCall($data);\n }\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundAnswer) {\n return $this->answerOutboundCall($data);\n }\n\n throw new LogicException('Unable to answer - not supported activity answer instance');\n }\n\n private function answerInboundCall(VO\\ConferenceManager\\SoftPhoneInboundAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $dateTime = now();\n\n $activity = $this->create(\n new VO\\ConferenceManager\\SoftPhoneInboundCreate(\n $data->getCallerId(),\n $data->getCallSid(),\n $data->getUser()\n )\n );\n\n /** @var Models\\Participant $activityGuest */\n $activityGuest = $activity->from;\n\n $activityGuest\n ->setEnterTime($dateTime)\n ->save();\n\n /** @var Connection $connection */\n $connection = $activityGuest->connections()\n ->where('type', Connection::TYPE_PSTN)\n ->firstOrFail();\n\n $connection\n ->setTelephonyProviderId($data->getCallSid())\n ->setOpenedAt($dateTime)\n ->save();\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n return $this->joinTwilioConference($connection, [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]);\n }\n\n private function answerOutboundCall(VO\\ConferenceManager\\SoftPhoneOutboundAnswer $data): TwiML\\VoiceResponse\n {\n $connection = $data->getConnection();\n $participant = $connection->getParticipant();\n $activity = $data->getActivity();\n $organiser = $activity->organizer;\n $dateTime = now();\n\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n 'connection' => $connection->id_string,\n 'participant' => $participant->id_string,\n 'activity' => $activity->id_string,\n ]);\n\n if (! $participant->hasEnterTime()) {\n $participant->update([\n 'enter_time' => $dateTime,\n ]);\n }\n\n $connection->update([\n 'is_muted' => $participant->isCoach(),\n 'opened_at' => $dateTime,\n ]);\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n if ($participant->isCoach()) {\n /** @var Connection $roomOwnerConnection */\n $roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'coach' => $roomOwnerConnection->getTelephonyProviderId(),\n ]\n );\n }\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]\n );\n }\n\n private function determineUserCallerId(Models\\User $user): string\n {\n if ($user->hasCallerId()) {\n return $user->getCallerId();\n }\n\n // TODO: We should require a caller id to be verified before using the dialer\n return $user->getSoftPhoneNumber() ?? $user->phone;\n }\n\n private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool\n {\n $startsWith = '[HTTP 404] Unable to create record: The requested resource';\n $endsWith = 'was not found';\n\n return Str::startsWith($exceptionMessage, $startsWith)\n && Str::endsWith($exceptionMessage, $endsWith);\n }\n\n /**\n * @throws RestException\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOtherParty(Connection $callerConnection): self\n {\n $session = $callerConnection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $this->logger->debug(__METHOD__, [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {\n throw new UnexpectedValueException('Unexpected activity type');\n }\n\n $callee = $activity->to;\n $calleeId = $callee->getPhoneNumber();\n\n /** @var Connection $calleeConnection */\n $calleeConnection = $callee->connections()\n ->whereIn('type', [\n Connection::TYPE_PSTN,\n Connection::TYPE_WEBRTC,\n ])\n ->firstOrFail();\n\n $caller = $activity->from;\n\n // we need to take the number from the connection\n // as the one we store on participant level might be the one we call to the rep via PSTN mode\n $callerId = $callerConnection->getPhoneNumber();\n\n if (! $caller->is($callerConnection->getParticipant())) {\n throw new LogicException(sprintf(\n 'Unexpected caller participant. Expected: \"%s\", Actual: \"%s\"',\n $caller->id_string,\n $callerConnection->getParticipant()->id_string\n ));\n }\n\n $twilioConferenceSettings = [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ];\n\n $hasInboundRecording = $activity->isTypeSoftphoneInbound()\n && $roomOwner->checkSoftphoneInboundRecordPreference();\n\n $hasOutboundRecording = $activity->isTypeSoftPhone()\n && $roomOwner->checkSoftphoneOutboundRecordPreference();\n\n $isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;\n\n $isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()\n || $activity->hasRecordingReasonComplianceRestricted();\n\n if ($isRecordingEnabled && ! $isRecordingRestricted) {\n $twilioConferenceSettings = array_merge($twilioConferenceSettings, [\n 'record' => true,\n 'recordingChannels' => 'dual',\n 'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),\n 'recordingStatusCallbackMethod' => 'POST',\n 'recordingStatusCallbackEvent' => 'in-progress completed absent',\n ]);\n\n if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n $twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()\n ? 'outbound'\n : 'inbound';\n }\n }\n\n try {\n if ($activity->hasRecordingReasonCompliancePrompted()) {\n $this->logger->info('[Softphone] Request compliance prompt via outbound call', [\n 'activity_uuid' => $activity->getUuid(),\n 'session' => $session->id_string,\n 'conference_sid' => $session->getTelephonyProviderId(),\n ]);\n\n $callInstance = $twilio->createCall(\n $callerId,\n $calleeId,\n $this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [\n 'session' => $session->id_string,\n 'connection' => $calleeConnection->id_string,\n ]),\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $callInstance->sid,\n 'parent_connection_id' => $callerConnection->getId(),\n ]);\n } else {\n $twilioParticipantConnection = $twilio->createParticipantInstance(\n $session->getTelephonyProviderId(),\n $callerId,\n $calleeId,\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $twilioParticipantConnection->callSid,\n ]);\n }\n } catch (RestException $restException) {\n if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {\n throw $restException;\n }\n\n if (! $activity->isInFiniteState()) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n\n $message = __METHOD__ . ': activity.status -> cancelled';\n } else {\n $message = sprintf(\n __METHOD__ . ': not setting to cancelled, activity.status is %s',\n $activity->getStatus()\n );\n }\n\n $this->logger->info($message, [\n 'activity' => $activity->id_string,\n 'exception' => $restException,\n ]);\n }\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n return $this;\n }\n\n private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array\n {\n $session = $connection->getSession();\n\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => false,\n 'record' => false,\n 'waitUrl' => '',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]),\n 'statusCallbackEvent' => [\n 'initiated',\n 'ringing',\n 'answered',\n 'completed',\n ],\n 'statusCallbackMethod' => 'POST',\n 'conferenceStatusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n ]),\n 'conferenceStatusCallbackMethod' => 'POST',\n 'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function applyDefaultTwilioConferenceSettings(Models\\Session $session, array $override = []): array\n {\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => false,\n 'endConferenceOnExit' => false,\n 'waitUrl' => '',\n 'statusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n// 'speaker',\n ]),\n 'statusCallbackMethod' => 'POST',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function joinTwilioConference(Connection $connection, array $options = []): TwiML\\VoiceResponse\n {\n $session = $connection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n\n $this->logger->debug(__METHOD__, [\n 'connection' => $connection->id_string,\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n ]);\n\n $voiceResponse = new TwiML\\VoiceResponse();\n\n $voiceResponse->dial()\n ->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]))\n ->conference(\n $session->getIdString(),\n $this->applyDefaultTwilioConferenceSettings($session, $options)\n );\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'result' => $voiceResponse,\n ]);\n\n return $voiceResponse;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny_jupiter","depth":4,"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":"19","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"19","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":"SELECT * FROM teams WHERE id = 1;\n\nselect * from crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 283;\nSELECT * FROM crm_fields WHERE id = 2234;\nSELECT * FROM crm_field_values WHERE crm_field_id = 2234;\n\nselect * from crm_profiles where user_id = 143;\n\nselect * from record_types where crm_configuration_id = 39; # 0121K000001MHElQAO,0121K000001MHEqQAO\nselect * from business_processes where crm_configuration_id = 39;\n# 01941000000H669AAC, 01941000000H66JAAS\n\nselect * from record_type_field_values\n where record_type_id IN (24);\n\nselect * from crm_field_values where id IN (2730);\n\nselect * from crm_configurations where id = 39;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce'; #1035\n\n\nselect * from users where team_id = 1; # 222 group 3\nSELECT * FROM activities WHERE user_id = 222 order by id desc;\nselect * from sidekick_settings where team_id = 1;\nselect * from teams where id = 1;\nselect * from team_features where team_id = 1;\n\nselect * from activities where crm_configuration_id = 2\nand provider = 'ms-teams' and id = 608765;\n\nSELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id = '59523413338';\n\nselect * from sidekick_settings where team_id = 2;\n\nSELECT * FROM activities WHERE id = 608660;\nselect * from activity_summary_logs where activity_id = 608660;\nselect * from ai_prompts where transcription_id = 11214;\n\n# ********************************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('ed78a437-2804-450e-ab2f-56ab1c641346') = uuid;\n# id: 608818, crm: 59628809737\nSELECT * FROM activities WHERE uuid_to_bin('36b06e55-afdd-4782-8dee-c624cd0af191') = uuid;\n# id: 608821, crm: 59632069252\nSELECT ce.start_time, ce.end_time, a.id, a.uuid, crm_provider_id, calendar_event_id, title,\nplaybook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id,\nscheduled_start_time, scheduled_end_time, actual_start_time, actual_end_time, a.created_at\nFROM activities a\njoin calendar_events ce on a.calendar_event_id = ce.id\nWHERE a.id IN (608818, 608821);\n\nselect * from users where team_id = 1;\nselect * from team_settings where team_id = 1;\nselect * from crm_profiles where crm_configuration_id = 39 order by user_id;\n\nselect * from team_features where team_id = 1;\n\nselect * from users where team_id = 2;\n\nSELECT * FROM activities WHERE uuid_to_bin('ec7647e9-5225-458b-b475-f31aa2769204') = uuid; # 612639\n# Preslava N. Ivanova, grou id 3\n\nSELECT * FROM opportunities WHERE uuid_to_bin('a2928fe5-aec5-46cb-85d9-7654c89e46a6') = uuid;\n\nselect * from activities where opportunity_id = 344 and actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00';\n\nselect\n a.id,\n a.type,\n a.scheduled_start_time,\n a.actual_start_time,\n a.created_at,\n a.opportunity_id,\n a.status\nFROM activities a\nWHERE opportunity_id = 344\nand status IN ('completed', 'received', 'delivered')\nand (\n (a.actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')\nOR (a.created_at between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')\nOR (a.scheduled_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00'))\n;\n\nSELECT * FROM users WHERE id = 222;\n\nSELECT * FROM crm_profiles WHERE user_id = 222;\nselect * from crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 281;\n\nselect * from group_deal_risk_types;\n\nselect * from opportunities where team_id = 1;\n\nSELECT * FROM opportunities WHERE id = 315;\nSELECT * FROM crm_field_data WHERE object_id = 315;\nselect * from crm_field_data where object_id = 260;\n\nselect * from generic_ai_prompts where subject_id = 315;\n\nselect * from teams; # 36, 21, 121, james.graham@bullhorn.jiminny.com\nSELECT * FROM social_accounts WHERE sociable_id = 121 and provider = 'bullhorn';\n\n# ************************************************************************************\nselect * from teams where id = 1;\nselect * from crm_configurations where id = 39;\nselect * from users where team_id = 1;\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 1;\n# 1 - 00541000004281rAAA\n# 204 - 0052g000003freeAAA\n# 429 - 0052g000003qGOiAAM\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce';\n\nselect * from activities where type = 'softphone'\nand created_at > '2024-12-11 15:24:36' order by id desc;\n\nselect * from activity_providers where team_id = 1;\nselect * from activity_provider_users where activity_provider_id = 328;\n\nselect * from opportunities where crm_configuration_id = 39\nAND account_id = 178 AND is_closed = false\norder by created_at DESC;\n\nselect * from contacts where id = 3952;\nselect * from accounts where id = 178;\n\n# ************************************************************************************\nselect * from teams where id = 36;\nselect * from crm_configurations where id = 21;\nselect * from users where team_id = 36;\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 36;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 36\nand sa.provider = 'bullhorn';\n\nselect * from social_accounts where id = 348;\nUPDATE social_accounts SET\nprovider_user_token = '21442_6802599_91:41179a58-21e7-4d7c-ad58-56bb666b2f65',\nprovider_refresh_token = '21442_6802599_91:01c6b335-3f2a-42e4-85ff-8a08fa65fceb',\nexpires = 1733998131,\nstate = 'connected'\nWHERE id = 348;\n\n# ************************************************************************************\nselect * from teams where id = 31;\nselect * from crm_configurations where id = 18;\n\nselect * from users where team_id = 31; # 257\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 31;\n\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 31\nand sa.provider = 'close';\n\nselect * from contacts where crm_configuration_id = 18;\n\n# ********************** NEPTUNE **************************************************************\nselect * from teams;\nselect * from users where id IN (1030, 1035, 1052);\nselect * from crm_configurations;\n\nselect * from users where team_id = 65; # 257\nselect * from team_settings where team_id = 65; # 257\nselect * from invitations where team_id = 65; # 257\nselect * from users where email = 'integration-account@jiminny.com'; # 257\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 65;\n\nselect * from crm_configurations where id = 53;\nselect * from accounts where crm_configuration_id = 53 order by id desc;\nselect * from leads where crm_configuration_id = 53 order by id desc;\nselect * from contacts where crm_configuration_id = 53 order by id desc;\nselect * from opportunities where crm_configuration_id = 53 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 53 order by id desc;\nselect * from crm_fields where crm_configuration_id = 53 order by id desc;\nselect * from crm_field_values where crm_field_id = 3341 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 53 order by id desc;\nselect * from stages where crm_configuration_id = 53 order by id desc;\n\n\nselect * from crm_profiles where crm_configuration_id = 13;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 65\nand sa.provider = 'integration-app';\n\nselect * from contacts where crm_configuration_id = 13;\n\nselect * from social_accounts where sociable_id = 283;\n\nSELECT * FROM opportunities WHERE crm_provider_id = '006O400000E9bzeIAB';\n\nselect * from activity_providers where team_id = 65;\nSELECT * FROM activities WHERE crm_configuration_id IN (51, 52, 53);\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 65\n;\n\n# ***************************** STAGING ********************************************\nSELECT * FROM teams;\nSELECT * FROM teams WHERE id = 88;\nSELECT * FROM teams WHERE id = 89;\nselect * from team_settings where team_id = 89;\nSELECT * FROM users WHERE team_id = 89;\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 89;\n\nselect * from users;\nSELECT * FROM social_accounts WHERE sociable_id = 1761;\nSELECT * FROM crm_configurations WHERE id = 70;\nselect * from accounts where crm_configuration_id = 70 order by id desc;\nselect * from leads where crm_configuration_id = 70 order by id desc;\nselect * from contacts where crm_configuration_id = 70 order by id desc;\nselect * from opportunities where crm_configuration_id = 70 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 70 order by id desc;\nselect * from crm_fields where crm_configuration_id = 70 order by id desc;\nselect * from crm_field_values where crm_field_id = 3536 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 70 order by id desc;\nselect * from stages where crm_configuration_id = 70 order by id desc;\nselect * from business_processes where crm_configuration_id = 70 order by id desc;\nselect * from business_process_stages where business_process_id = 34;\n\nselect * from contacts where id = 10468;\n\nselect * from crm_layouts where crm_configuration_id = 70;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 388;\nSELECT * FROM crm_fields WHERE id IN (3533,3534,3535);\n\nselect * from activities where crm_configuration_id = 70\nand (account_id IS NOT NULL or lead_id IS NOT NULL or contact_id IS NOT NULL or opportunity_id IS NOT NULL) order by id desc;\n\nSELECT * FROM activities WHERE uuid_to_bin('2e10b60f-8a61-41c5-a3d4-28835353dc65') = uuid;\nSELECT * FROM activities where crm_configuration_id = 69 ;\n\nSELECT * FROM users WHERE email LIKE '%jiminny_web_sa2@jiminny.com%';\nSELECT * FROM activities WHERE uuid_to_bin('5a150c93-40fc-42ec-b3bd-c1d328e09f6e') = uuid;\nSELECT * FROM opportunities WHERE id = 385;\n\nselect * from participants p\njoin activities a on p.activity_id = a.id\nwhere a.crm_configuration_id = 70\nand (p.lead_id IS NOT NULL or p.contact_id IS NOT NULL);\nSELECT * FROM participants WHERE id = 1013638;\n\nselect * from teams where id = 90;\nselect * from users where team_id = 90;\nselect * from social_accounts where social_accounts.sociable_id IN (1960,1760);\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 71;\nselect * from invitations where team_id = 90;\n\nselect * from crm_configurations where id = 71;\nselect * from accounts where crm_configuration_id = 71 order by id desc;\nselect * from leads where crm_configuration_id = 71 order by id desc;\nselect * from contacts where crm_configuration_id = 71 order by id desc;\nselect * from opportunities where crm_configuration_id = 71 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 71 order by id desc;\nselect * from crm_fields where crm_configuration_id = 71 order by id desc;\nselect * from crm_field_values where crm_field_id = 3341 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 71 order by id desc;\nselect * from stages where crm_configuration_id = 71 order by id desc;\n\nselect * from users order by secondary_email desc;\nselect u.id, u.email, u.status, sa.id, sa.provider_user_id from social_accounts sa\n join users u on sa.sociable_id = u.id\nwhere sa.provider = 'google' and u.email LIKE 'aneliya%';\n\nselect * from failed_jobs order by id desc;\n\nselect * from users where email = 'ben.allwright@learningpeople.co.uk' or secondary_email = 'ben.allwright@learningpeople.co.uk';\n\nselect * from teams;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 39;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type = 'task';\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce';\n\n# ************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('c38b3895-fd0f-4b1f-9fb2-c170dba137c6') = uuid;\nSELECT * FROM crm_configurations WHERE id = 70;\n\nselect * from teams where id = 1;\nselect * from groups where team_id = 1;\nselect * from users where team_id = 1;\n\nselect o.id, o.name,o.close_date, u.id, u.name, u.group_id, r.id, r.display_name, g.name, g.scope from opportunities o\njoin users u on o.user_id = u.id\njoin groups g on u.group_id = g.id\njoin role_user ru on u.id = ru.user_id\njoin roles r on ru.role_id = r.id\nwhere o.crm_configuration_id = 39 and close_date > '2024-01-01 00:00:00';\n\nselect * from role_user where user_id = 143;\nselect * from roles;\n\nselect * from role_user;\nselect * from groups where id = 9;\nselect * from scope_groups where group_id = 9;\n\n# ************************************************************************************\nselect * from teams where id = 36;\nselect * from crm_configurations;\nSELECT * FROM social_accounts WHERE sociable_id = 121;\n\nhttps://crmsandbox.zoho.com/crm/jiminnyw4/tab/Leads/4776201000005049105\nhttps://crmsandbox.zoho.com/crm/\n\nhttps://crm.zoho.com/crm/org3469620/tab/Leads/230045000229559080\n https://crm.zoho.com/crm/\n org3469620\n\nSELECT * FROM activities WHERE uuid_to_bin('03382d20-c8bc-48e7-a3d4-90b52fa5ceab') = uuid;\n\nselect * from users where email LIKE \"%mobile_automation_%\";\nselect * from social_accounts where sociable_id IN (2228);\nselect * from crm_profiles where user_id IN (2222,2223,2226,2227);\n\nselect * from teams order by id desc;\nSELECT * FROM users WHERE id = 2229;\nSELECT * FROM crm_profiles WHERE user_id = 2229;\nselect * from opportunities where crm_configuration_id = 88;\nselect * from crm_fields where crm_configuration_id = 88;\nselect * from crm_profiles where crm_configuration_id = 88;\n\nSELECT * FROM teams WHERE id = 1;\n\nSELECT * FROM users WHERE id = 143;\nSELECT * FROM users WHERE uuid_to_bin('fde193d3-06a2-4e1a-8895-62b94039215d') = uuid;\nSELECT * FROM teams WHERE uuid_to_bin('73385071-a756-42ae-9c73-8b53f2309467') = uuid;\n\nhttps://app.staging.jiminny.com/ondemand?\n min_duration=1\n &\n only_recorded=1\n &\n user_id%5B%5D=641f1acb-16b8-42d1-8726-df52979dad0e\n &\n sequence_number=2\n\n select * from users where team_id = 1 and email like '%stoyan%'\n\nselect * from coaching_feedbacks;\n\nselect * from teams;\nSELECT * FROM users WHERE team_id = 36;\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 19\nand sa.provider = 'pipedrive';\n\nselect * from users where id = 143;\n\nSELECT * FROM users WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\nSELECT * FROM teams WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\nSELECT * FROM activity_shares WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\n\nselect * from users where team_id = 2;\nselect * from activities where crm_configuration_id = 39\nand activities.scheduled_start_time BETWEEN '2025-04-09 00:00:00' AND '2025-04-09 23:59:59'\nAND user_id = 143\norder by id desc;\n\n# ************************************************************************************\nselect * from teams where id = 142; # 2312, 126\nselect * from team_settings;\nselect * from users where team_id = 142; # 21642\nSELECT * FROM social_accounts WHERE sociable_id = 21642;\nSELECT * FROM crm_profiles cp join users u ON u.id = cp.user_id WHERE team_id = 142;\nselect * from crm_profiles where id IN (93);\nselect * from invitations;\nselect * from team_features where team_id = 1;\n\nSELECT * FROM crm_configurations WHERE id = 126;\nselect * from accounts where crm_configuration_id = 126 order by id desc;\nselect * from leads where crm_configuration_id = 126 order by id desc;\nselect * from contacts where crm_configuration_id = 126 order by id desc;\nselect * from opportunities where crm_configuration_id = 126 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 126 order by id desc;\nselect * from crm_fields where crm_configuration_id = 126 # 11060\n# and type IN ('picklist', 'status')\n# and object_type = 'task'\norder by id desc;\n# 5731,5732,5733\nselect DISTINCT crm_field_id from crm_field_values where crm_field_id IN (11151,12239,12215,12185,12175,12165,12144,12137,12127,12109,12107,12105,12103,12092,12037,12005,12003,11987,11969,11958,11951,11942,11931,11924,11921,11917,11915,11901,11893,11883,11872,11870,11868,11866,11839,11833,11821,11793,11780,11777,11769,11757,11737,11735,11656,11645,11638,11629,11618,11611,11602,11591,11584,11581,11558,11544,11543,11534,11532,11529,11527,11503,11497,11493,11488,11470,11468,11457,11455,11397,11387,11372,11363,11348,11323,11318,11309,11301,11300,11292,11290,11286,11284,11256,11252,11242,11237,11233,11219,11176,11160) order by id desc;\nselect * from crm_layouts where crm_configuration_id = 126 order by id desc;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id in (300,299,298);\nselect * from stages where crm_configuration_id = 126 order by id desc;\nselect * from business_processes where crm_configuration_id = 126 order by id desc;\nselect * from business_process_stages where business_process_id IN (76,75,74,73);\nselect * from playbooks where team_id = 142;\nselect * from playbook_layouts where playbook_id IN (108);\nSELECT * FROM playbook_categories WHERE playbook_id IN (108);\n\nselect * from teams where id = 130;\nSELECT * FROM social_accounts WHERE sociable_id = 2291;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 2\nand sa.provider = 'hubspot';\n\nSELECT * FROM activities\n WHERE crm_configuration_id = 110;\n\nselect * from teams;\nselect * from crm_configurations;\n\nSELECT * FROM activities WHERE id = 628773;\nSELECT * FROM crm_profiles WHERE user_id = 1460;\nSELECT * FROM social_accounts WHERE sociable_id = 2291;\n\nselect * from teams;\nselect ru.*, pr.*, p.* from users u join role_user ru on ru.user_id = u.id\njoin permission_role pr on pr.role_id = ru.role_id\n join permissions p on p.id = pr.permission_id\nwhere team_id = 495 and p.name IN ('dial');\n\nselect * from teams where id = 145;\nselect * from crm_configurations where id = 129;\nselect * from social_accounts where sociable_id = 2317;\nSELECT * FROM activities WHERE uuid_to_bin('8dbab184-a333-4268-ad57-fb41f8d53a9a') = uuid;\n\nselect * from teams where id = 1;\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 280;\nSELECT * FROM crm_layout_entities WHERE id = 5507;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type IN ('event');\n\nselect * from teams;\nselect * from activities where crm_configuration_id = 14;\n\nSELECT * FROM social_accounts where provider = 'copper';\n\nselect * from activities where id = 628467;\nselect * from participants where activity_id = 628467;\n\nSELECT * FROM contacts WHERE id = 3969;\nSELECT * FROM accounts WHERE id = 177;\n\nSELECT * FROM activities WHERE uuid_to_bin('4eb54c77-cfa3-2bd4-84a7-9ed46a21c988') = uuid;\n\n# ********************* BH\nselect * from teams where id = 36;\nSELECT * FROM crm_configurations WHERE id = 21;\nselect * from activities where crm_configuration_id = 21 and id = 607901;\nselect * from activities where crm_configuration_id = 21;\n\nselect * roles;\nselect * from permissions;\nselect * from permission_role where permission_id = 226;\n\nselect * from migrations order by id desc;\n\n# mercury\n# neptune\n# earth\n\nselect * from teams;\nselect * from teams where id = 19;\nselect * from teams where id = 27;\nselect * from users where team_id = 27;\nSELECT * FROM crm_configurations WHERE id = 42;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 19\nand sa.provider = 'pipedrive';\n\nselect * from activities where id = 631461;\nSELECT * FROM crm_field_values WHERE crm_field_id = 180;\n\nselect * from teams where id = 2;\nSELECT * FROM social_accounts WHERE sociable_id = 89;\n\nSELECT * FROM activities WHERE uuid_to_bin('ba0c029a-bc14-4e17-8603-64174acebcbb') = uuid; # 634273\nselect * from activity_summary_logs where activity_id = 634273;\n\nselect * from sidekick_settings where team_id = 2;\n\nselect * from teams; # 2, 2\nSELECT * FROM crm_configurations WHERE team_id = 2; # 2\nselect * from team_features where team_id = 2;\nselect * from features;\nSELECT * FROM opportunities WHERE crm_configuration_id = 2 and crm_provider_id = '51317301383';\nSELECT * FROM opportunities WHERE crm_configuration_id = 2 order by id desc;\n\nselect * from automated_reports order by id desc;\nselect * from automated_report_results order by id desc;\nselect * from users where team_id = 1 and id IN (7160, 3248);\nselect * from migrations order by id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1052 and sa.provider = 'hubspot';\n\nselect * from teams where id = 1;\nselect * from groups g JOIN playbooks p on g.playbook_id = p.id where g.team_id = 1;\nselect * from groups where id = 565;\nselect * from playbooks where team_id = 1;\nselect * from playbooks where id = 175;\nselect * from playbook_categories where playbook_id = 175;\nselect * from users where team_id = 1052;\nselect * from users where id = 7160;\nselect * from crm_profiles where user_id = 7160;\nselect * from features;\nselect\n *\n# id, uuid, type, provider, playbook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id, stage_id,\n# crm_configuration_id, crm_provider_id, transcription_id, status\nfrom activities where crm_configuration_id = 1 and type = 'conference'\n# and crm_provider_id IS NOT NULL\nand provider != 'uploader' and actual_start_time IS NOT NULL\nORDER by id desc;\nselect * from activities where id = 54747783; # 00UO400000pCzojMAC\n\nselect p.id, p.activity_type, pc.id, pc.name\nFROM playbooks p\njoin playbook_categories pc on p.id = pc.playbook_id\nwhere p.team_id = 1 and p.activity_type = 'event';\n\nSELECT * FROM crm_fields WHERE crm_configuration_id = 1 and object_type = 'event';\nSELECT * FROM crm_field_values WHERE crm_field_id = 4;\n\nselect * from crm_layouts cl join playbook_layouts pl on cl.id = pl.layout_id\nwhere crm_configuration_id = 1 and pl.playbook_id = 175;\n\nselect * from teams;\nSELECT r.* FROM automated_reports r\njoin teams t on r.team_id = t.id\nWHERE r.frequency = 'daily'\n and r.status = 1\nAND t.status = 'active'\nAND (r.expires_at >= now() OR r.expires_at IS NULL);\n\nselect * from automated_report_results where report_id IN (18, 33);\n\nselect * from users where team_id = 1 and id = 1047;\nSELECT * FROM social_accounts WHERE sociable_id = 1047;\n\nselect * from activity_searches where id = 10932;\nselect * from activity_search_filters where activity_search_id = 10932;\nselect * from automated_reports order by id desc;\nselect * from automated_report_results order by id desc;\nselect * from automated_reports where id IN (55);\nselect * from automated_report_results where id IN (81);\nselect * from users where id IN (10633, 13987, 11985);\nselect * from users where group_id IN (3710);\n\nSELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;\nSELECT * FROM automated_report_results WHERE uuid_to_bin('582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;\n\nselect * from teams;\nselect * from accounts where team_id = 1;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2;\nSELECT * FROM automated_report_results WHERE uuid_to_bin('82e74956-6144-4cd1-a3d3-af985c3070a4') = uuid;\n\nselect * from teams where id = 1029;\nselect * from crm_configurations where provider = 'pipedrive';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1029 and sa.provider = 'pipedrive';\n\n[\n {\n \"user_id\": \"23460 (owner)\",\n \"email\": \"integration-account@pipedrive.jiminny.com\",\n \"id\": 69,\n \"sociable_id\": 23460,\n \"provider_user_id\": \"19555731\",\n \"provider_user_token\": \"v1u:AQIBAHj-LzTNK2yuuuaLqifzhWb9crUNKTpk4FlQ9rjnXqp_6AEQhDhDQVa1nvWCHEvnpvSEAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMnG8KNcZLIjEnlRPxAgEQgDsGPIcKjsMU7Qel36BtM5FCQa56mYUy24_AAoqd12yjVFkq6egLqS0inp-5G4JE7frJURMV8VTw_fY14g:rYyABXmXsBhEdYU_dfmDH8GF-vzSseJXE5bds_zAyVdAwlTXOPdTl9i4PS4jVofLvpq7IRgvEt2BzGhR6cWiQXHHD0AtayQOkZm262ClFCsMYtKGfep0Jq1n0eiRIVqT9gAY7rWvhpEDF8oAlgnRGmqx-euIkpzE79sPXh4OjNx_yUIaanjyplGpBBJ_NiACd1sMlZmseyUyyU_gldqlCBAuK9hhATc9icgg7zuoc7nKBBrlg49tRtzEagDh6xbFBpzfCp7kE_7n4TLWfWHLPMzu-bktjwA969G9sFRmoH1GjPA0a2odDT4dk1_1ouBrB1NcTT2hQUqH-_RzDW2nWmeoVHA\",\n \"provider_refresh_token\": \"5034113:19555731:87c14258f0c813d02767ee975f6044d46b6b2bfc\",\n \"expires\": 1779091997,\n \"refresh_token_expires\": null,\n \"provider\": \"pipedrive\",\n \"state\": \"connected\",\n \"auth_scope\": \"base,deals:full,activities:full,contacts:full,search:read\",\n \"retry_after\": null,\n \"created_at\": \"2025-04-16 08:23:28\",\n \"updated_at\": \"2026-05-18 07:13:18\",\n \"provider_user_token_encrypted\": \"eyJpdiI6Ik9oYzBwcTVteWN4ajBRaDdMb0FjWGc9PSIsInZhbHVlIjoiQUNMbjU5UHR2Q1RnbWRXMEU1TUx6UVpRU3gzdVc5WktmTE4rVlhXa3lRaGhyUUlZMVZRWEJuR0JaWWhZZXNPRlBuanhuYU5MVHZ5TG9NTVVjNjJSUUpZZ2VhbGYxbHg0UzJEdTVmQjNFZnJRZkMwYjd0N2RMZ05GRS9IR3c2K0NoVjBjYTA5UG12SkxHeDlDMGVNN3ptUWUzblFiTngyYzR1eUpHSEYrdEwvZm0yZklGYjBSNEtTR2ZGazhLM1hqT3lVbjFobDNPZ0d3anFlcjhKcXpERlZGOVN1YmFyNDIvM1BFRDFMYnp4WEI4TGVLM2xYcy9CL0RDOWlQNHI3N0NwWnJPMWRtZllmM2ZKeE9TV2NBeDZxL2M1YnlSVzd3ZW96T3F1QmtBYXBHYkUraGcvdCtRajlYZXBjVVBSdFlMUjVPVDVJbDdyenVoWjMrbVJvU3ZZR1dQZ3pqQ3F3aDF2U2xPZWZIN3BCWVFLNXpxQUtDb2pIK0xHc1E3SklSM3Q0VkNSbm9oK2pFK3hLaW54T1dWbEVneUp0RGhhdVBuOFI4MXROc3dZWnFnTUpiaDVMMnZ4QmlRM3k4QWFlMVFWUFYvdGhJQzZBYnhBQmhWa2ZKN1ZhSnhpaExxbmlTemgzUWw3aXJtWjlSMlhmd0lWMDNDN1N3My9Ua0hCL2xqY3RkVFhMSjFJMDRjOE5DWlgrZ3FMVXN6RWlwUE5GWERZdG1xVjVxOFlLRGs2VVJKS3FLeWRxQjYxdDh4Z2JJNXhlWEZ3dkQ4SGtybDNUcndzblFHeVJNRkYraFh2UDFIUTdMQ1BZa3dEU1dBbzk5K0dyT2RNVFBZZUJpRytSck5pYlI1YUZyMmhUNEZCdWxHYmJLREtzbUpjVkhvV0RoejJpbm1SWHlNaXE5M0RhcS94UW9EdFoweWF3bVFVY0dTZWFPSXBmOCtDYndia215cmtyZU1oT0Exam1CV2tPblNhYjY4clVEeUs3anVHUmNHeS9YLzRha1VhbGl3N3lwaDlzZnRhQUZta2s4eFllNHgzRklSRmNyazJ4dlBDMnByNCtBRjNaVjJCTT0iLCJtYWMiOiJkYTQxZDY1OTY2ZTljMTgyZWRhNGEzMGZjZDc2MjBjN2NlNzU2YTViNGQxNWE1NTI2ZmI1MWQyYjQ2ODYxZmEwIiwidGFnIjoiIn0=\",\n \"provider_refresh_token_encrypted\": \"eyJpdiI6Imt2bGxlSmxUK05GMjF0dEtPaTMrUlE9PSIsInZhbHVlIjoiUXZHTElZRXkreHFxUU02SnZ4eVBacG9WWTRlVkd0USszV3JyVFlpU2ZaY25GSWo2WFcrRzNlbFRlUXRQczNwSXRCcEdvZEpveG9jMzBDSTgwdnZLQnc9PSIsIm1hYyI6ImQxYWI0NzU5Nzg5MDI4YWVhZmQ4Mjg1YzhkZDQzMjRkYWYwYTdhZTY5MDMxMjc0OWNiYjY0Nzc3NDQ0MTEyY2EiLCJ0YWciOiIifQ==\",\n \"encryption_key\": \"0x01020300786192D9D96DD60D3D1EBC9DFAEE5F0C45EE80165CF3F3D2B3B627026AA7CFC7CA0128DD463535D32EE491ADBADF8B07596B0000006E306C06092A864886F70D010706A05F305D020100305806092A864886F70D010701301E060960864801650304012E3011040C7FB1319048ECB2EA3F23B995020110802B52D22F5EAD341AB238FBBCB6093156E443876A664BA5555D722565C2B2D04422C868CD7350555E3CC74221\",\n \"sociable_type\": \"user\",\n \"owner_id\": 23460\n },\n {\n \"user_id\": \"23463\",\n \"email\": \"jiminny_web_sa@pipedrive.jiminny.com\",\n \"id\": 72,\n \"sociable_id\": 23463,\n \"provider_user_id\": \"23270841\",\n \"provider_user_token\": \"v1u:AQIBAHj-LzTNK2yuuuaLqifzhWb9crUNKTpk4FlQ9rjnXqp_6AEQhDhDQVa1nvWCHEvnpvSEAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMnG8KNcZLIjEnlRPxAgEQgDsGPIcKjsMU7Qel36BtM5FCQa56mYUy24_AAoqd12yjVFkq6egLqS0inp-5G4JE7frJURMV8VTw_fY14g:txqCuWLeVhgwiaFqXIvQWWrtrnFIy_4lT3VCr-sRB4g6_2lK8KcQ_6ka_qFmZhr-IeIAKmAImx6H1iIKx4Ab7XZ5MnKIh03GKbHjI0n0rGT9UYkeH21jKOxJvKaSX_TyLbxFPR6irPQyvqcpBClXI52gVJH01KzZy_dMTHFXhhDLOIhEpSrQXHG7Qo8q7OuBeSPZrU1ALi9kmAm4GTpzGTbzfl-hUt9IAfPunJOCyv3mf2Tl-MT8EyQgOFPrsP3yz9UlSyVhb40zO_bCnUrqNxjwgm_E6vgpVrwULTQbHe-43Dq-TRetjceUzT88GBgVJ0UdxXP5BRBVXcL5ir8-9ZTMaNU\",\n \"provider_refresh_token\": \"5034113:23270841:34790b44838f5fd422c2d2c82e00f1ecf2178ad1\",\n \"expires\": 1753837219,\n \"refresh_token_expires\": null,\n \"provider\": \"pipedrive\",\n \"state\": \"full-refresh\",\n \"auth_scope\": \"base,deals:full,activities:full,contacts:full,search:read\",\n \"retry_after\": null,\n \"created_at\": \"2025-04-16 10:41:12\",\n \"updated_at\": \"2025-07-30 01:00:17\",\n \"provider_user_token_encrypted\": \"eyJpdiI6InVEVEl2SzIxMjd1bkNDWG9lY3YvQ0E9PSIsInZhbHVlIjoiRlJLbDZ3ZHo2dHMxUWQwdGU2YUZjV3lPWHlxQTJ0eWh4S0RTMjlSaThVcktoaWhXcTd2UXl4Mjg4bW5UUnZCZEQ5NXpuQTVKOURiMDNIa3l3K0lSWCt5azRBOEdHbitWQXhxeEpMVUZ1dnF2NVl1TTZLelZOaGNvRlBLeGpIWlpoRGloUEprd2xoNUFPcTlLcUd3WVlUU2svR1NOeXBYQ2NnMWhnK1V2aytOeVVMNzJGdVZXMU1JS3BSaGNCcmxkVjEwQ01mQXNBdWNhS1lMZWZobnZwcHBkN1R4bUVDRUNYdnNtSVJkdG5MdkN3djJBT0hMRDl1MHFGbnl4WU13ejBkUWsrdUljWkZhcTJlN0JDSGdTVzlPY0FGMUJlZi9WRGpkMUk5R20wblYwSk9VR2FuaHpLTzJNeitMbnF1V1NEMUUxRmYzaDRGZDMyaXd3Z0FjQno4SzVoYjUyTmV3UXNpRlF4TGZKNVl3dUFqdGhKVWJjRVlUUXRGalBpaXJvSGJmbS8rd25aQ2Zvc05saHRDRjlHV1VEUFFiRU5xbWRVNkxFSXV0RUZyaUlYNWMwMmhnZ0Y1VUQ4eEFkY2QyWXpnR0NidkVJSWdjVGljUjA3NDdpZW9WUHhtSlpGSGdNU2hRUTA5ZW03RGpiRFdrdytkOHJXSUxlazZNaG9iaUg2NFZYdEc3YklMeFZvblBzWmVRSDcvN2M4WUNtL3h4S2IrUW00cXhialBia3BJcW5XallBNlV5WWdhcE1ZVFBBM3RSSUVBamYvOGZEQWM5a2FJMGwyWkRSU3dlTEtPemZlQ3RXRGNDcEdYRHdMNTlTQVg2SUdUN0hVaXdZT1dUOUlrVzkrZHFBemhwWXg1cm4rSy9UOHUzcnNSM2dISlhBRTEyVUNyTkZYaFJYUlN1a2RKVHhhM2dHckR3a1JGaVZ2Wk9BVnhTclVpSEhwWTVpQTlJbXVkOUxSTGpRbDk0a1VtbE9QMVAvN3VlVEhJUHd2elowd0laNVIzZ3M3N0ZOK0NDakIwQ2FicGxoMDBhTXI0VUppUmFOZHdlWUdGV21NNFQ4RU1nY0dnWT0iLCJtYWMiOiI0ZTU4ZmJkNTdkYmY0NGE4NTJjZjlhNDExNzE2YjhlOWM2NTA5OGY2MDA4OTcyZDVlOTljY2JjZDhmMjBhMDUyIiwidGFnIjoiIn0=\",\n \"provider_refresh_token_encrypted\": \"eyJpdiI6IkJGMkdRVHRKM2VTcitKNGcvQWJtUGc9PSIsInZhbHVlIjoicmFyRkxPZ1Rybm1ORXlEVjJ2TXFGTzJtM3hWaFhnUVVHN2ZlR1lRM0I5d3ozbnZTcU5EdzJqRkI2elhyNWhlenRTUXV3bjk0N1JXeklDMVAyUzBKcHc9PSIsIm1hYyI6ImJhODVjMGQyZDE3Y2ExNjc2OWVjYWJiNmFjYWVkODIyMTMyNWVhYTExMTgwYTEyMTU0MzUzZGE1YjQ5YzQ5ZjEiLCJ0YWciOiIifQ==\",\n \"encryption_key\": \"0x01020300788C37CDF66ABE9301A68D5D4866AC0C197E04EEE4D904F20A59991322EBAE252301BEF4B24FF01359E934E5654617E4EEAC0000006E306C06092A864886F70D010706A05F305D020100305806092A864886F70D010701301E060960864801650304012E3011040CEB00EF45BDEA999A5558889E020110802B925F372F7BEFAF0B45E48686113D1DA430ECFCC07B1F61BA6828CC44235278EDB05C486E8068D0BE737D91\",\n \"sociable_type\": \"user\",\n \"owner_id\": 23460\n }\n]","depth":4,"on_screen":true,"value":"SELECT * FROM teams WHERE id = 1;\n\nselect * from crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 283;\nSELECT * FROM crm_fields WHERE id = 2234;\nSELECT * FROM crm_field_values WHERE crm_field_id = 2234;\n\nselect * from crm_profiles where user_id = 143;\n\nselect * from record_types where crm_configuration_id = 39; # 0121K000001MHElQAO,0121K000001MHEqQAO\nselect * from business_processes where crm_configuration_id = 39;\n# 01941000000H669AAC, 01941000000H66JAAS\n\nselect * from record_type_field_values\n where record_type_id IN (24);\n\nselect * from crm_field_values where id IN (2730);\n\nselect * from crm_configurations where id = 39;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce'; #1035\n\n\nselect * from users where team_id = 1; # 222 group 3\nSELECT * FROM activities WHERE user_id = 222 order by id desc;\nselect * from sidekick_settings where team_id = 1;\nselect * from teams where id = 1;\nselect * from team_features where team_id = 1;\n\nselect * from activities where crm_configuration_id = 2\nand provider = 'ms-teams' and id = 608765;\n\nSELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id = '59523413338';\n\nselect * from sidekick_settings where team_id = 2;\n\nSELECT * FROM activities WHERE id = 608660;\nselect * from activity_summary_logs where activity_id = 608660;\nselect * from ai_prompts where transcription_id = 11214;\n\n# ********************************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('ed78a437-2804-450e-ab2f-56ab1c641346') = uuid;\n# id: 608818, crm: 59628809737\nSELECT * FROM activities WHERE uuid_to_bin('36b06e55-afdd-4782-8dee-c624cd0af191') = uuid;\n# id: 608821, crm: 59632069252\nSELECT ce.start_time, ce.end_time, a.id, a.uuid, crm_provider_id, calendar_event_id, title,\nplaybook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id,\nscheduled_start_time, scheduled_end_time, actual_start_time, actual_end_time, a.created_at\nFROM activities a\njoin calendar_events ce on a.calendar_event_id = ce.id\nWHERE a.id IN (608818, 608821);\n\nselect * from users where team_id = 1;\nselect * from team_settings where team_id = 1;\nselect * from crm_profiles where crm_configuration_id = 39 order by user_id;\n\nselect * from team_features where team_id = 1;\n\nselect * from users where team_id = 2;\n\nSELECT * FROM activities WHERE uuid_to_bin('ec7647e9-5225-458b-b475-f31aa2769204') = uuid; # 612639\n# Preslava N. Ivanova, grou id 3\n\nSELECT * FROM opportunities WHERE uuid_to_bin('a2928fe5-aec5-46cb-85d9-7654c89e46a6') = uuid;\n\nselect * from activities where opportunity_id = 344 and actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00';\n\nselect\n a.id,\n a.type,\n a.scheduled_start_time,\n a.actual_start_time,\n a.created_at,\n a.opportunity_id,\n a.status\nFROM activities a\nWHERE opportunity_id = 344\nand status IN ('completed', 'received', 'delivered')\nand (\n (a.actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')\nOR (a.created_at between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')\nOR (a.scheduled_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00'))\n;\n\nSELECT * FROM users WHERE id = 222;\n\nSELECT * FROM crm_profiles WHERE user_id = 222;\nselect * from crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 281;\n\nselect * from group_deal_risk_types;\n\nselect * from opportunities where team_id = 1;\n\nSELECT * FROM opportunities WHERE id = 315;\nSELECT * FROM crm_field_data WHERE object_id = 315;\nselect * from crm_field_data where object_id = 260;\n\nselect * from generic_ai_prompts where subject_id = 315;\n\nselect * from teams; # 36, 21, 121, james.graham@bullhorn.jiminny.com\nSELECT * FROM social_accounts WHERE sociable_id = 121 and provider = 'bullhorn';\n\n# ************************************************************************************\nselect * from teams where id = 1;\nselect * from crm_configurations where id = 39;\nselect * from users where team_id = 1;\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 1;\n# 1 - 00541000004281rAAA\n# 204 - 0052g000003freeAAA\n# 429 - 0052g000003qGOiAAM\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce';\n\nselect * from activities where type = 'softphone'\nand created_at > '2024-12-11 15:24:36' order by id desc;\n\nselect * from activity_providers where team_id = 1;\nselect * from activity_provider_users where activity_provider_id = 328;\n\nselect * from opportunities where crm_configuration_id = 39\nAND account_id = 178 AND is_closed = false\norder by created_at DESC;\n\nselect * from contacts where id = 3952;\nselect * from accounts where id = 178;\n\n# ************************************************************************************\nselect * from teams where id = 36;\nselect * from crm_configurations where id = 21;\nselect * from users where team_id = 36;\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 36;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 36\nand sa.provider = 'bullhorn';\n\nselect * from social_accounts where id = 348;\nUPDATE social_accounts SET\nprovider_user_token = '21442_6802599_91:41179a58-21e7-4d7c-ad58-56bb666b2f65',\nprovider_refresh_token = '21442_6802599_91:01c6b335-3f2a-42e4-85ff-8a08fa65fceb',\nexpires = 1733998131,\nstate = 'connected'\nWHERE id = 348;\n\n# ************************************************************************************\nselect * from teams where id = 31;\nselect * from crm_configurations where id = 18;\n\nselect * from users where team_id = 31; # 257\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 31;\n\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 31\nand sa.provider = 'close';\n\nselect * from contacts where crm_configuration_id = 18;\n\n# ********************** NEPTUNE **************************************************************\nselect * from teams;\nselect * from users where id IN (1030, 1035, 1052);\nselect * from crm_configurations;\n\nselect * from users where team_id = 65; # 257\nselect * from team_settings where team_id = 65; # 257\nselect * from invitations where team_id = 65; # 257\nselect * from users where email = 'integration-account@jiminny.com'; # 257\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 65;\n\nselect * from crm_configurations where id = 53;\nselect * from accounts where crm_configuration_id = 53 order by id desc;\nselect * from leads where crm_configuration_id = 53 order by id desc;\nselect * from contacts where crm_configuration_id = 53 order by id desc;\nselect * from opportunities where crm_configuration_id = 53 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 53 order by id desc;\nselect * from crm_fields where crm_configuration_id = 53 order by id desc;\nselect * from crm_field_values where crm_field_id = 3341 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 53 order by id desc;\nselect * from stages where crm_configuration_id = 53 order by id desc;\n\n\nselect * from crm_profiles where crm_configuration_id = 13;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 65\nand sa.provider = 'integration-app';\n\nselect * from contacts where crm_configuration_id = 13;\n\nselect * from social_accounts where sociable_id = 283;\n\nSELECT * FROM opportunities WHERE crm_provider_id = '006O400000E9bzeIAB';\n\nselect * from activity_providers where team_id = 65;\nSELECT * FROM activities WHERE crm_configuration_id IN (51, 52, 53);\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 65\n;\n\n# ***************************** STAGING ********************************************\nSELECT * FROM teams;\nSELECT * FROM teams WHERE id = 88;\nSELECT * FROM teams WHERE id = 89;\nselect * from team_settings where team_id = 89;\nSELECT * FROM users WHERE team_id = 89;\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 89;\n\nselect * from users;\nSELECT * FROM social_accounts WHERE sociable_id = 1761;\nSELECT * FROM crm_configurations WHERE id = 70;\nselect * from accounts where crm_configuration_id = 70 order by id desc;\nselect * from leads where crm_configuration_id = 70 order by id desc;\nselect * from contacts where crm_configuration_id = 70 order by id desc;\nselect * from opportunities where crm_configuration_id = 70 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 70 order by id desc;\nselect * from crm_fields where crm_configuration_id = 70 order by id desc;\nselect * from crm_field_values where crm_field_id = 3536 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 70 order by id desc;\nselect * from stages where crm_configuration_id = 70 order by id desc;\nselect * from business_processes where crm_configuration_id = 70 order by id desc;\nselect * from business_process_stages where business_process_id = 34;\n\nselect * from contacts where id = 10468;\n\nselect * from crm_layouts where crm_configuration_id = 70;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 388;\nSELECT * FROM crm_fields WHERE id IN (3533,3534,3535);\n\nselect * from activities where crm_configuration_id = 70\nand (account_id IS NOT NULL or lead_id IS NOT NULL or contact_id IS NOT NULL or opportunity_id IS NOT NULL) order by id desc;\n\nSELECT * FROM activities WHERE uuid_to_bin('2e10b60f-8a61-41c5-a3d4-28835353dc65') = uuid;\nSELECT * FROM activities where crm_configuration_id = 69 ;\n\nSELECT * FROM users WHERE email LIKE '%jiminny_web_sa2@jiminny.com%';\nSELECT * FROM activities WHERE uuid_to_bin('5a150c93-40fc-42ec-b3bd-c1d328e09f6e') = uuid;\nSELECT * FROM opportunities WHERE id = 385;\n\nselect * from participants p\njoin activities a on p.activity_id = a.id\nwhere a.crm_configuration_id = 70\nand (p.lead_id IS NOT NULL or p.contact_id IS NOT NULL);\nSELECT * FROM participants WHERE id = 1013638;\n\nselect * from teams where id = 90;\nselect * from users where team_id = 90;\nselect * from social_accounts where social_accounts.sociable_id IN (1960,1760);\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 71;\nselect * from invitations where team_id = 90;\n\nselect * from crm_configurations where id = 71;\nselect * from accounts where crm_configuration_id = 71 order by id desc;\nselect * from leads where crm_configuration_id = 71 order by id desc;\nselect * from contacts where crm_configuration_id = 71 order by id desc;\nselect * from opportunities where crm_configuration_id = 71 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 71 order by id desc;\nselect * from crm_fields where crm_configuration_id = 71 order by id desc;\nselect * from crm_field_values where crm_field_id = 3341 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 71 order by id desc;\nselect * from stages where crm_configuration_id = 71 order by id desc;\n\nselect * from users order by secondary_email desc;\nselect u.id, u.email, u.status, sa.id, sa.provider_user_id from social_accounts sa\n join users u on sa.sociable_id = u.id\nwhere sa.provider = 'google' and u.email LIKE 'aneliya%';\n\nselect * from failed_jobs order by id desc;\n\nselect * from users where email = 'ben.allwright@learningpeople.co.uk' or secondary_email = 'ben.allwright@learningpeople.co.uk';\n\nselect * from teams;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 39;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type = 'task';\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce';\n\n# ************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('c38b3895-fd0f-4b1f-9fb2-c170dba137c6') = uuid;\nSELECT * FROM crm_configurations WHERE id = 70;\n\nselect * from teams where id = 1;\nselect * from groups where team_id = 1;\nselect * from users where team_id = 1;\n\nselect o.id, o.name,o.close_date, u.id, u.name, u.group_id, r.id, r.display_name, g.name, g.scope from opportunities o\njoin users u on o.user_id = u.id\njoin groups g on u.group_id = g.id\njoin role_user ru on u.id = ru.user_id\njoin roles r on ru.role_id = r.id\nwhere o.crm_configuration_id = 39 and close_date > '2024-01-01 00:00:00';\n\nselect * from role_user where user_id = 143;\nselect * from roles;\n\nselect * from role_user;\nselect * from groups where id = 9;\nselect * from scope_groups where group_id = 9;\n\n# ************************************************************************************\nselect * from teams where id = 36;\nselect * from crm_configurations;\nSELECT * FROM social_accounts WHERE sociable_id = 121;\n\nhttps://crmsandbox.zoho.com/crm/jiminnyw4/tab/Leads/4776201000005049105\nhttps://crmsandbox.zoho.com/crm/\n\nhttps://crm.zoho.com/crm/org3469620/tab/Leads/230045000229559080\n https://crm.zoho.com/crm/\n org3469620\n\nSELECT * FROM activities WHERE uuid_to_bin('03382d20-c8bc-48e7-a3d4-90b52fa5ceab') = uuid;\n\nselect * from users where email LIKE \"%mobile_automation_%\";\nselect * from social_accounts where sociable_id IN (2228);\nselect * from crm_profiles where user_id IN (2222,2223,2226,2227);\n\nselect * from teams order by id desc;\nSELECT * FROM users WHERE id = 2229;\nSELECT * FROM crm_profiles WHERE user_id = 2229;\nselect * from opportunities where crm_configuration_id = 88;\nselect * from crm_fields where crm_configuration_id = 88;\nselect * from crm_profiles where crm_configuration_id = 88;\n\nSELECT * FROM teams WHERE id = 1;\n\nSELECT * FROM users WHERE id = 143;\nSELECT * FROM users WHERE uuid_to_bin('fde193d3-06a2-4e1a-8895-62b94039215d') = uuid;\nSELECT * FROM teams WHERE uuid_to_bin('73385071-a756-42ae-9c73-8b53f2309467') = uuid;\n\nhttps://app.staging.jiminny.com/ondemand?\n min_duration=1\n &\n only_recorded=1\n &\n user_id%5B%5D=641f1acb-16b8-42d1-8726-df52979dad0e\n &\n sequence_number=2\n\n select * from users where team_id = 1 and email like '%stoyan%'\n\nselect * from coaching_feedbacks;\n\nselect * from teams;\nSELECT * FROM users WHERE team_id = 36;\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 19\nand sa.provider = 'pipedrive';\n\nselect * from users where id = 143;\n\nSELECT * FROM users WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\nSELECT * FROM teams WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\nSELECT * FROM activity_shares WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\n\nselect * from users where team_id = 2;\nselect * from activities where crm_configuration_id = 39\nand activities.scheduled_start_time BETWEEN '2025-04-09 00:00:00' AND '2025-04-09 23:59:59'\nAND user_id = 143\norder by id desc;\n\n# ************************************************************************************\nselect * from teams where id = 142; # 2312, 126\nselect * from team_settings;\nselect * from users where team_id = 142; # 21642\nSELECT * FROM social_accounts WHERE sociable_id = 21642;\nSELECT * FROM crm_profiles cp join users u ON u.id = cp.user_id WHERE team_id = 142;\nselect * from crm_profiles where id IN (93);\nselect * from invitations;\nselect * from team_features where team_id = 1;\n\nSELECT * FROM crm_configurations WHERE id = 126;\nselect * from accounts where crm_configuration_id = 126 order by id desc;\nselect * from leads where crm_configuration_id = 126 order by id desc;\nselect * from contacts where crm_configuration_id = 126 order by id desc;\nselect * from opportunities where crm_configuration_id = 126 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 126 order by id desc;\nselect * from crm_fields where crm_configuration_id = 126 # 11060\n# and type IN ('picklist', 'status')\n# and object_type = 'task'\norder by id desc;\n# 5731,5732,5733\nselect DISTINCT crm_field_id from crm_field_values where crm_field_id IN (11151,12239,12215,12185,12175,12165,12144,12137,12127,12109,12107,12105,12103,12092,12037,12005,12003,11987,11969,11958,11951,11942,11931,11924,11921,11917,11915,11901,11893,11883,11872,11870,11868,11866,11839,11833,11821,11793,11780,11777,11769,11757,11737,11735,11656,11645,11638,11629,11618,11611,11602,11591,11584,11581,11558,11544,11543,11534,11532,11529,11527,11503,11497,11493,11488,11470,11468,11457,11455,11397,11387,11372,11363,11348,11323,11318,11309,11301,11300,11292,11290,11286,11284,11256,11252,11242,11237,11233,11219,11176,11160) order by id desc;\nselect * from crm_layouts where crm_configuration_id = 126 order by id desc;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id in (300,299,298);\nselect * from stages where crm_configuration_id = 126 order by id desc;\nselect * from business_processes where crm_configuration_id = 126 order by id desc;\nselect * from business_process_stages where business_process_id IN (76,75,74,73);\nselect * from playbooks where team_id = 142;\nselect * from playbook_layouts where playbook_id IN (108);\nSELECT * FROM playbook_categories WHERE playbook_id IN (108);\n\nselect * from teams where id = 130;\nSELECT * FROM social_accounts WHERE sociable_id = 2291;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 2\nand sa.provider = 'hubspot';\n\nSELECT * FROM activities\n WHERE crm_configuration_id = 110;\n\nselect * from teams;\nselect * from crm_configurations;\n\nSELECT * FROM activities WHERE id = 628773;\nSELECT * FROM crm_profiles WHERE user_id = 1460;\nSELECT * FROM social_accounts WHERE sociable_id = 2291;\n\nselect * from teams;\nselect ru.*, pr.*, p.* from users u join role_user ru on ru.user_id = u.id\njoin permission_role pr on pr.role_id = ru.role_id\n join permissions p on p.id = pr.permission_id\nwhere team_id = 495 and p.name IN ('dial');\n\nselect * from teams where id = 145;\nselect * from crm_configurations where id = 129;\nselect * from social_accounts where sociable_id = 2317;\nSELECT * FROM activities WHERE uuid_to_bin('8dbab184-a333-4268-ad57-fb41f8d53a9a') = uuid;\n\nselect * from teams where id = 1;\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 280;\nSELECT * FROM crm_layout_entities WHERE id = 5507;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type IN ('event');\n\nselect * from teams;\nselect * from activities where crm_configuration_id = 14;\n\nSELECT * FROM social_accounts where provider = 'copper';\n\nselect * from activities where id = 628467;\nselect * from participants where activity_id = 628467;\n\nSELECT * FROM contacts WHERE id = 3969;\nSELECT * FROM accounts WHERE id = 177;\n\nSELECT * FROM activities WHERE uuid_to_bin('4eb54c77-cfa3-2bd4-84a7-9ed46a21c988') = uuid;\n\n# ********************* BH\nselect * from teams where id = 36;\nSELECT * FROM crm_configurations WHERE id = 21;\nselect * from activities where crm_configuration_id = 21 and id = 607901;\nselect * from activities where crm_configuration_id = 21;\n\nselect * roles;\nselect * from permissions;\nselect * from permission_role where permission_id = 226;\n\nselect * from migrations order by id desc;\n\n# mercury\n# neptune\n# earth\n\nselect * from teams;\nselect * from teams where id = 19;\nselect * from teams where id = 27;\nselect * from users where team_id = 27;\nSELECT * FROM crm_configurations WHERE id = 42;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 19\nand sa.provider = 'pipedrive';\n\nselect * from activities where id = 631461;\nSELECT * FROM crm_field_values WHERE crm_field_id = 180;\n\nselect * from teams where id = 2;\nSELECT * FROM social_accounts WHERE sociable_id = 89;\n\nSELECT * FROM activities WHERE uuid_to_bin('ba0c029a-bc14-4e17-8603-64174acebcbb') = uuid; # 634273\nselect * from activity_summary_logs where activity_id = 634273;\n\nselect * from sidekick_settings where team_id = 2;\n\nselect * from teams; # 2, 2\nSELECT * FROM crm_configurations WHERE team_id = 2; # 2\nselect * from team_features where team_id = 2;\nselect * from features;\nSELECT * FROM opportunities WHERE crm_configuration_id = 2 and crm_provider_id = '51317301383';\nSELECT * FROM opportunities WHERE crm_configuration_id = 2 order by id desc;\n\nselect * from automated_reports order by id desc;\nselect * from automated_report_results order by id desc;\nselect * from users where team_id = 1 and id IN (7160, 3248);\nselect * from migrations order by id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1052 and sa.provider = 'hubspot';\n\nselect * from teams where id = 1;\nselect * from groups g JOIN playbooks p on g.playbook_id = p.id where g.team_id = 1;\nselect * from groups where id = 565;\nselect * from playbooks where team_id = 1;\nselect * from playbooks where id = 175;\nselect * from playbook_categories where playbook_id = 175;\nselect * from users where team_id = 1052;\nselect * from users where id = 7160;\nselect * from crm_profiles where user_id = 7160;\nselect * from features;\nselect\n *\n# id, uuid, type, provider, playbook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id, stage_id,\n# crm_configuration_id, crm_provider_id, transcription_id, status\nfrom activities where crm_configuration_id = 1 and type = 'conference'\n# and crm_provider_id IS NOT NULL\nand provider != 'uploader' and actual_start_time IS NOT NULL\nORDER by id desc;\nselect * from activities where id = 54747783; # 00UO400000pCzojMAC\n\nselect p.id, p.activity_type, pc.id, pc.name\nFROM playbooks p\njoin playbook_categories pc on p.id = pc.playbook_id\nwhere p.team_id = 1 and p.activity_type = 'event';\n\nSELECT * FROM crm_fields WHERE crm_configuration_id = 1 and object_type = 'event';\nSELECT * FROM crm_field_values WHERE crm_field_id = 4;\n\nselect * from crm_layouts cl join playbook_layouts pl on cl.id = pl.layout_id\nwhere crm_configuration_id = 1 and pl.playbook_id = 175;\n\nselect * from teams;\nSELECT r.* FROM automated_reports r\njoin teams t on r.team_id = t.id\nWHERE r.frequency = 'daily'\n and r.status = 1\nAND t.status = 'active'\nAND (r.expires_at >= now() OR r.expires_at IS NULL);\n\nselect * from automated_report_results where report_id IN (18, 33);\n\nselect * from users where team_id = 1 and id = 1047;\nSELECT * FROM social_accounts WHERE sociable_id = 1047;\n\nselect * from activity_searches where id = 10932;\nselect * from activity_search_filters where activity_search_id = 10932;\nselect * from automated_reports order by id desc;\nselect * from automated_report_results order by id desc;\nselect * from automated_reports where id IN (55);\nselect * from automated_report_results where id IN (81);\nselect * from users where id IN (10633, 13987, 11985);\nselect * from users where group_id IN (3710);\n\nSELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;\nSELECT * FROM automated_report_results WHERE uuid_to_bin('582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;\n\nselect * from teams;\nselect * from accounts where team_id = 1;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2;\nSELECT * FROM automated_report_results WHERE uuid_to_bin('82e74956-6144-4cd1-a3d3-af985c3070a4') = uuid;\n\nselect * from teams where id = 1029;\nselect * from crm_configurations where provider = 'pipedrive';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1029 and sa.provider = 'pipedrive';\n\n[\n {\n \"user_id\": \"23460 (owner)\",\n \"email\": \"integration-account@pipedrive.jiminny.com\",\n \"id\": 69,\n \"sociable_id\": 23460,\n \"provider_user_id\": \"19555731\",\n \"provider_user_token\": \"v1u:AQIBAHj-LzTNK2yuuuaLqifzhWb9crUNKTpk4FlQ9rjnXqp_6AEQhDhDQVa1nvWCHEvnpvSEAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMnG8KNcZLIjEnlRPxAgEQgDsGPIcKjsMU7Qel36BtM5FCQa56mYUy24_AAoqd12yjVFkq6egLqS0inp-5G4JE7frJURMV8VTw_fY14g:rYyABXmXsBhEdYU_dfmDH8GF-vzSseJXE5bds_zAyVdAwlTXOPdTl9i4PS4jVofLvpq7IRgvEt2BzGhR6cWiQXHHD0AtayQOkZm262ClFCsMYtKGfep0Jq1n0eiRIVqT9gAY7rWvhpEDF8oAlgnRGmqx-euIkpzE79sPXh4OjNx_yUIaanjyplGpBBJ_NiACd1sMlZmseyUyyU_gldqlCBAuK9hhATc9icgg7zuoc7nKBBrlg49tRtzEagDh6xbFBpzfCp7kE_7n4TLWfWHLPMzu-bktjwA969G9sFRmoH1GjPA0a2odDT4dk1_1ouBrB1NcTT2hQUqH-_RzDW2nWmeoVHA\",\n \"provider_refresh_token\": \"5034113:19555731:87c14258f0c813d02767ee975f6044d46b6b2bfc\",\n \"expires\": 1779091997,\n \"refresh_token_expires\": null,\n \"provider\": \"pipedrive\",\n \"state\": \"connected\",\n \"auth_scope\": \"base,deals:full,activities:full,contacts:full,search:read\",\n \"retry_after\": null,\n \"created_at\": \"2025-04-16 08:23:28\",\n \"updated_at\": \"2026-05-18 07:13:18\",\n \"provider_user_token_encrypted\": \"eyJpdiI6Ik9oYzBwcTVteWN4ajBRaDdMb0FjWGc9PSIsInZhbHVlIjoiQUNMbjU5UHR2Q1RnbWRXMEU1TUx6UVpRU3gzdVc5WktmTE4rVlhXa3lRaGhyUUlZMVZRWEJuR0JaWWhZZXNPRlBuanhuYU5MVHZ5TG9NTVVjNjJSUUpZZ2VhbGYxbHg0UzJEdTVmQjNFZnJRZkMwYjd0N2RMZ05GRS9IR3c2K0NoVjBjYTA5UG12SkxHeDlDMGVNN3ptUWUzblFiTngyYzR1eUpHSEYrdEwvZm0yZklGYjBSNEtTR2ZGazhLM1hqT3lVbjFobDNPZ0d3anFlcjhKcXpERlZGOVN1YmFyNDIvM1BFRDFMYnp4WEI4TGVLM2xYcy9CL0RDOWlQNHI3N0NwWnJPMWRtZllmM2ZKeE9TV2NBeDZxL2M1YnlSVzd3ZW96T3F1QmtBYXBHYkUraGcvdCtRajlYZXBjVVBSdFlMUjVPVDVJbDdyenVoWjMrbVJvU3ZZR1dQZ3pqQ3F3aDF2U2xPZWZIN3BCWVFLNXpxQUtDb2pIK0xHc1E3SklSM3Q0VkNSbm9oK2pFK3hLaW54T1dWbEVneUp0RGhhdVBuOFI4MXROc3dZWnFnTUpiaDVMMnZ4QmlRM3k4QWFlMVFWUFYvdGhJQzZBYnhBQmhWa2ZKN1ZhSnhpaExxbmlTemgzUWw3aXJtWjlSMlhmd0lWMDNDN1N3My9Ua0hCL2xqY3RkVFhMSjFJMDRjOE5DWlgrZ3FMVXN6RWlwUE5GWERZdG1xVjVxOFlLRGs2VVJKS3FLeWRxQjYxdDh4Z2JJNXhlWEZ3dkQ4SGtybDNUcndzblFHeVJNRkYraFh2UDFIUTdMQ1BZa3dEU1dBbzk5K0dyT2RNVFBZZUJpRytSck5pYlI1YUZyMmhUNEZCdWxHYmJLREtzbUpjVkhvV0RoejJpbm1SWHlNaXE5M0RhcS94UW9EdFoweWF3bVFVY0dTZWFPSXBmOCtDYndia215cmtyZU1oT0Exam1CV2tPblNhYjY4clVEeUs3anVHUmNHeS9YLzRha1VhbGl3N3lwaDlzZnRhQUZta2s4eFllNHgzRklSRmNyazJ4dlBDMnByNCtBRjNaVjJCTT0iLCJtYWMiOiJkYTQxZDY1OTY2ZTljMTgyZWRhNGEzMGZjZDc2MjBjN2NlNzU2YTViNGQxNWE1NTI2ZmI1MWQyYjQ2ODYxZmEwIiwidGFnIjoiIn0=\",\n \"provider_refresh_token_encrypted\": \"eyJpdiI6Imt2bGxlSmxUK05GMjF0dEtPaTMrUlE9PSIsInZhbHVlIjoiUXZHTElZRXkreHFxUU02SnZ4eVBacG9WWTRlVkd0USszV3JyVFlpU2ZaY25GSWo2WFcrRzNlbFRlUXRQczNwSXRCcEdvZEpveG9jMzBDSTgwdnZLQnc9PSIsIm1hYyI6ImQxYWI0NzU5Nzg5MDI4YWVhZmQ4Mjg1YzhkZDQzMjRkYWYwYTdhZTY5MDMxMjc0OWNiYjY0Nzc3NDQ0MTEyY2EiLCJ0YWciOiIifQ==\",\n \"encryption_key\": \"0x01020300786192D9D96DD60D3D1EBC9DFAEE5F0C45EE80165CF3F3D2B3B627026AA7CFC7CA0128DD463535D32EE491ADBADF8B07596B0000006E306C06092A864886F70D010706A05F305D020100305806092A864886F70D010701301E060960864801650304012E3011040C7FB1319048ECB2EA3F23B995020110802B52D22F5EAD341AB238FBBCB6093156E443876A664BA5555D722565C2B2D04422C868CD7350555E3CC74221\",\n \"sociable_type\": \"user\",\n \"owner_id\": 23460\n },\n {\n \"user_id\": \"23463\",\n \"email\": \"jiminny_web_sa@pipedrive.jiminny.com\",\n \"id\": 72,\n \"sociable_id\": 23463,\n \"provider_user_id\": \"23270841\",\n \"provider_user_token\": \"v1u:AQIBAHj-LzTNK2yuuuaLqifzhWb9crUNKTpk4FlQ9rjnXqp_6AEQhDhDQVa1nvWCHEvnpvSEAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMnG8KNcZLIjEnlRPxAgEQgDsGPIcKjsMU7Qel36BtM5FCQa56mYUy24_AAoqd12yjVFkq6egLqS0inp-5G4JE7frJURMV8VTw_fY14g:txqCuWLeVhgwiaFqXIvQWWrtrnFIy_4lT3VCr-sRB4g6_2lK8KcQ_6ka_qFmZhr-IeIAKmAImx6H1iIKx4Ab7XZ5MnKIh03GKbHjI0n0rGT9UYkeH21jKOxJvKaSX_TyLbxFPR6irPQyvqcpBClXI52gVJH01KzZy_dMTHFXhhDLOIhEpSrQXHG7Qo8q7OuBeSPZrU1ALi9kmAm4GTpzGTbzfl-hUt9IAfPunJOCyv3mf2Tl-MT8EyQgOFPrsP3yz9UlSyVhb40zO_bCnUrqNxjwgm_E6vgpVrwULTQbHe-43Dq-TRetjceUzT88GBgVJ0UdxXP5BRBVXcL5ir8-9ZTMaNU\",\n \"provider_refresh_token\": \"5034113:23270841:34790b44838f5fd422c2d2c82e00f1ecf2178ad1\",\n \"expires\": 1753837219,\n \"refresh_token_expires\": null,\n \"provider\": \"pipedrive\",\n \"state\": \"full-refresh\",\n \"auth_scope\": \"base,deals:full,activities:full,contacts:full,search:read\",\n \"retry_after\": null,\n \"created_at\": \"2025-04-16 10:41:12\",\n \"updated_at\": \"2025-07-30 01:00:17\",\n \"provider_user_token_encrypted\": \"eyJpdiI6InVEVEl2SzIxMjd1bkNDWG9lY3YvQ0E9PSIsInZhbHVlIjoiRlJLbDZ3ZHo2dHMxUWQwdGU2YUZjV3lPWHlxQTJ0eWh4S0RTMjlSaThVcktoaWhXcTd2UXl4Mjg4bW5UUnZCZEQ5NXpuQTVKOURiMDNIa3l3K0lSWCt5azRBOEdHbitWQXhxeEpMVUZ1dnF2NVl1TTZLelZOaGNvRlBLeGpIWlpoRGloUEprd2xoNUFPcTlLcUd3WVlUU2svR1NOeXBYQ2NnMWhnK1V2aytOeVVMNzJGdVZXMU1JS3BSaGNCcmxkVjEwQ01mQXNBdWNhS1lMZWZobnZwcHBkN1R4bUVDRUNYdnNtSVJkdG5MdkN3djJBT0hMRDl1MHFGbnl4WU13ejBkUWsrdUljWkZhcTJlN0JDSGdTVzlPY0FGMUJlZi9WRGpkMUk5R20wblYwSk9VR2FuaHpLTzJNeitMbnF1V1NEMUUxRmYzaDRGZDMyaXd3Z0FjQno4SzVoYjUyTmV3UXNpRlF4TGZKNVl3dUFqdGhKVWJjRVlUUXRGalBpaXJvSGJmbS8rd25aQ2Zvc05saHRDRjlHV1VEUFFiRU5xbWRVNkxFSXV0RUZyaUlYNWMwMmhnZ0Y1VUQ4eEFkY2QyWXpnR0NidkVJSWdjVGljUjA3NDdpZW9WUHhtSlpGSGdNU2hRUTA5ZW03RGpiRFdrdytkOHJXSUxlazZNaG9iaUg2NFZYdEc3YklMeFZvblBzWmVRSDcvN2M4WUNtL3h4S2IrUW00cXhialBia3BJcW5XallBNlV5WWdhcE1ZVFBBM3RSSUVBamYvOGZEQWM5a2FJMGwyWkRSU3dlTEtPemZlQ3RXRGNDcEdYRHdMNTlTQVg2SUdUN0hVaXdZT1dUOUlrVzkrZHFBemhwWXg1cm4rSy9UOHUzcnNSM2dISlhBRTEyVUNyTkZYaFJYUlN1a2RKVHhhM2dHckR3a1JGaVZ2Wk9BVnhTclVpSEhwWTVpQTlJbXVkOUxSTGpRbDk0a1VtbE9QMVAvN3VlVEhJUHd2elowd0laNVIzZ3M3N0ZOK0NDakIwQ2FicGxoMDBhTXI0VUppUmFOZHdlWUdGV21NNFQ4RU1nY0dnWT0iLCJtYWMiOiI0ZTU4ZmJkNTdkYmY0NGE4NTJjZjlhNDExNzE2YjhlOWM2NTA5OGY2MDA4OTcyZDVlOTljY2JjZDhmMjBhMDUyIiwidGFnIjoiIn0=\",\n \"provider_refresh_token_encrypted\": \"eyJpdiI6IkJGMkdRVHRKM2VTcitKNGcvQWJtUGc9PSIsInZhbHVlIjoicmFyRkxPZ1Rybm1ORXlEVjJ2TXFGTzJtM3hWaFhnUVVHN2ZlR1lRM0I5d3ozbnZTcU5EdzJqRkI2elhyNWhlenRTUXV3bjk0N1JXeklDMVAyUzBKcHc9PSIsIm1hYyI6ImJhODVjMGQyZDE3Y2ExNjc2OWVjYWJiNmFjYWVkODIyMTMyNWVhYTExMTgwYTEyMTU0MzUzZGE1YjQ5YzQ5ZjEiLCJ0YWciOiIifQ==\",\n \"encryption_key\": \"0x01020300788C37CDF66ABE9301A68D5D4866AC0C197E04EEE4D904F20A59991322EBAE252301BEF4B24FF01359E934E5654617E4EEAC0000006E306C06092A864886F70D010706A05F305D020100305806092A864886F70D010701301E060960864801650304012E3011040CEB00EF45BDEA999A5558889E020110802B925F372F7BEFAF0B45E48686113D1DA430ECFCC07B1F61BA6828CC44235278EDB05C486E8068D0BE737D91\",\n \"sociable_type\": \"user\",\n \"owner_id\": 23460\n }\n]","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}]...
|
7498689834597179426
|
6686367646540542029
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\Twilio\Conference\ConferenceManager;
use Illuminate\Database\Eloquent;
use Illuminate\Support\Str;
use Jiminny\Component\ElasticSearch\Model\Observer;
use Jiminny\Component\Twilio\Conference\ConferenceManager;
use Jiminny\Component\Twilio\Exception\OutOfBoundsException;
use Jiminny\Component\Twilio\Exception\UnexpectedValueException;
use Jiminny\Component\Twilio\Resolver;
use Jiminny\Component\Twilio\VO;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Events;
use Jiminny\Exceptions\LogicException;
use Jiminny\Models;
use Jiminny\Models\Participant\Connection;
use Jiminny\Services\ActivityService;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Crm\CachedCrmServiceDecorator;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Services\Telephony\TwilioClientBuilder;
use Psr\Log\LoggerInterface;
use Twilio\Exceptions\ConfigurationException;
use Twilio\Exceptions\RestException;
use Twilio\Exceptions\TwilioException;
use Twilio\TwiML;
final readonly class SoftPhoneManager implements ConferenceManager
{
public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;
public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';
public function __construct(
private ProviderRegistry $crmProviderRegistry,
private Webhook $urlGenerator,
private ActivityService $activityService,
private LoggerInterface $logger,
private TwilioClientBuilder $twilioClientBuilder,
) {
}
public function create(VO\ConferenceManager\ActivityCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'data' => $data,
'instanceof' => get_class($data),
]);
$activity = Observer::skipEvents(function () use ($data) {
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundCreate) {
$activity = $this->createInboundActivity($data);
} elseif ($data instanceof VO\ConferenceManager\SoftPhoneOutboundCreate) {
$activity = $this->createOutboundActivity($data);
} else {
throw new LogicException(sprintf(
'Unknown softphone implementation: %s',
get_class($data)
));
}
return $activity;
});
$this->logger->debug(__METHOD__ . ': done', [
'instanceof' => get_class($data),
'activity' => $activity->id_string,
]);
return $activity;
}
private function createOutboundActivity(VO\ConferenceManager\SoftPhoneOutboundCreate $data): Models\Activity
{
$startTime = microtime(true);
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
$now = now();
$user = $data->getUser();
$team = $user->getTeam();
$device = $data->getDevice();
$phoneNumber = $data->getPhoneNumber();
$route = $data->getPhoneRoute();
if (! $this->activityService->teamCanDial($team, $phoneNumber)) {
$region = getRegionByNumber($phoneNumber, $user->getCountryCode());
throw new LogicException(sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? 'this number'
));
}
$lead = null;
$account = null;
$contact = null;
$stage = null;
$opportunity = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $user,
'providerSlug' => $team->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
} catch (\Throwable $throwable) {
$this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [
'exception' => $throwable,
'user_id' => $user->getId(),
'team_id' => $team->getId(),
]);
$crmService = $this->crmProviderRegistry->get($team->crm->provider);
if (! $user->isCrmRequired()) {
$crmService->setUser($user->team->owner);
} else {
$crmService->setUser($user);
}
}
$prospectId = $data->getProspectId();
try {
if ($prospectId !== null) {
$crmStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [
'prospect_id' => $prospectId,
'object_type' => $data->getObjectType(),
]);
[$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(
$prospectId,
$data->getObjectType()
);
$this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),
'found_lead' => $lead !== null,
'found_account' => $account !== null,
'found_opportunity' => $opportunity !== null,
'found_contact' => $contact !== null,
]);
// Try to attach a contact if we have an opportunity.
if ($opportunity !== null && $contact === null) {
$matchStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');
[, , , $contact, ] = $crmService->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
$this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),
'found_contact' => $contact !== null,
]);
}
} else {
$decorator = resolve(CachedCrmServiceDecorator::class);
$decorator->setCrmService($crmService);
[$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
}
} catch (\Throwable $throwable) {
$this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.
}
if ($data->hasProspectStage()) {
/** @var Models\Stage $stage */
$stage = $team->crm->stages()->uuid($data->getProspectStage());
}
$dbStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting activity creation in DB');
$activity = $user->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE,
'provider' => Models\Activity::PROVIDER_TWILIO,
'device_id' => $device->id ?? null,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'value' => $opportunity ? $opportunity->value : null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_PENDING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),
'scheduled_start_time' => $now,
]);
$this->logger->info('[SoftPhoneManager] Activity created in DB', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),
'activity_id' => $activity->id ?? null,
]);
if (! $activity instanceof Models\Activity) {
throw new LogicException('Activity model expected');
}
$regionId = null;
if ($user->hasRegionId()) {
$regionId = $user->getRegionId();
} elseif ($data->hasRegion()) {
$regionId = $data->getRegion()->getId();
}
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $regionId,
'is_muted' => false,
]);
$phoneRouteResolver = new Resolver\PhoneRouteResolver($user, $route);
$phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();
$connectionType = $phoneRouteResolver->getConnectionType();
$callerId = $this->determineUserCallerId($user);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'user_id' => $user->id,
'email' => $user->email,
'name' => $user->name,
'phone_number' => $phoneRouteResolver->mustUsePSTN()
? $phoneNumberOrigin
: $callerId,
]);
$caller->connections()->create([
'session_id' => $session->getId(),
'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',
'phone' => $callerId,
'type' => $connectionType,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $phoneNumber,
'name' => $activity->prospect_name,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'phone' => $phoneNumber,
'direction' => 'outbound-api',
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$activity->refresh();
$this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [
'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),
'activity_id' => $activity->id_string,
]);
$this->logger->debug(__METHOD__ . ': done', [
'data' => $data,
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
return $activity;
}
private function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$roomOwner = $data->getUser();
$team = $roomOwner->getTeam();
$phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();
$callerId = $data->getCallerId();
$callerCallSid = $data->getCallSid();
$now = now();
if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {
$region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());
throw new LogicException(
sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? ' this number',
),
);
}
$lead = $account = $opportunity = $contact = $stage = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $roomOwner->getTeam(),
'integrationAdmin' => $roomOwner,
'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
[$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(
$callerId,
null,
$roomOwner->getId()
);
} catch (\Throwable $throwable) {
$this->logger->error(__METHOD__ . ': crm exception', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening
}
/** @var Models\Activity $activity */
$activity = $roomOwner->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE_INBOUND,
'provider' => Models\Activity::PROVIDER_TWILIO,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_RINGING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),
'scheduled_start_time' => $now,
]);
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $roomOwner->getRegionId(),
'is_muted' => false,
]);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $callerId,
'name' => $activity->prospect_name,
'enter_time' => $now,
]);
$caller->connections()->create([
'telephony_provider_id' => $callerCallSid,
'session_id' => $session->getId(),
'phone' => $callerId,
'direction' => 'inbound',
'type' => Connection::TYPE_PSTN,
'opened_at' => $now,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'user_id' => $roomOwner->id,
'email' => $roomOwner->email,
'name' => $roomOwner->name,
'phone_number' => $phoneNumber,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'direction' => 'outbound-api',
'phone' => $phoneNumber,
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$this->logger->debug(__METHOD__ . ': done', [
'callSid' => $data->getCallSid(),
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
$activity->refresh();
return $activity;
}
private function getVoiceRecordingReasonCode(Models\Team $team, string $phoneNumber): int
{
$this->logger->debug(__METHOD__, [
'team' => $team->id_string,
'phone' => $phoneNumber,
]);
if (
! $team->hasComplianceModeRecordingPrompt()
&& ! $team->hasComplianceModeRecordingRestrictToOneSide()
&& ! $team->hasComplianceModeRecordingRestrictRecording()
) {
return 0;
}
/** @var Models\VoiceConsentPrefix|null $voiceConsentPolicy */
$voiceConsentPolicy = Models\VoiceConsentPrefix::query()
->whereRaw("? LIKE CONCAT(destination_prefix, '%')", [$phoneNumber])
->where(static function (Eloquent\Builder $builder) use ($team): void {
$builder
->whereNull('team_id')
->orWhere('team_id', $team->getId());
})
->orderByDesc('team_id')
->first();
if (! $voiceConsentPolicy instanceof Models\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {
return 0;
}
if ($team->hasComplianceModeRecordingPrompt()) {
// This number requires consent from the other party before recording the call.
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;
}
if ($team->hasComplianceModeRecordingRestrictToOneSide()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;
}
if ($team->hasComplianceModeRecordingRestrictRecording()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;
}
return 0;
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOrganiser(Models\Activity $activity): void
{
$this->logger->debug(__METHOD__, [
'activity' => $activity->id_string,
]);
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$canBeDialed =
$activity->isTypeSoftPhone()
&& ! $activity->hasStarted()
&& $activity->isPending()
&& $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->exists();
if (! $canBeDialed) {
throw new OutOfBoundsException('Cannot make an outbound call to this activity.');
}
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->firstOrFail();
/** @var Models\Session $session */
$session = $organiserConnection->getSession();
$twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
]);
// What number will the organizer see as caller id
$callerId = $roomOwner->getSoftPhoneNumber();
if ($callerId === null) {
$callee = $activity->participants()->whereNull('user_id')->first();
// This is a bridge call. We make it look like the callee is calling the organizer
$callerId = $callee->phone_number;
}
// What number will the prospect see as caller id
$calleeId = $organiser->getPhoneNumber();
$this->logger->debug(__METHOD__ . ': twilio call', [
'calleeId' => $calleeId,
'callerId' => $callerId,
]);
$twilioParticipant = $twilio->createParticipantInstance(
$session->id_string,
$callerId,
$calleeId,
$twilioConferenceSettings
);
$organiserConnection
->setTelephonyProviderId($twilioParticipant->callSid)
->save();
$session
->setTelephonyProviderId($twilioParticipant->conferenceSid)
->save();
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'session' => $session->id_string,
]);
}
public function dialCoach(Models\Activity $activity, Models\User $user): void
{
throw new \LogicException('Not implemented yet');
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function terminate(VO\ConferenceManager\ActivityTerminate $data): void
{
if (! $data instanceof VO\ConferenceManager\SoftphoneTerminate) {
throw new UnexpectedValueException(sprintf(
'Expecting an instance of "%s", but "%s" given.',
VO\ConferenceManager\SoftphoneTerminate::class,
\get_class($data)
));
}
$this->logger->debug(__METHOD__, [
'activity' => $data->getActivity()->id_string,
'user' => $data->getUser()->id_string,
]);
$activity = $data->getActivity();
$roomOwner = $activity->getUser();
$isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();
if ($isCancelling) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
}
/** @var Models\Session $session */
$session = $activity->sessions->first();
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->whereIn('type', [
Connection::TYPE_WEBRTC,
Connection::TYPE_PSTN,
])
->firstOrFail();
if (! $organiserConnection->hasTelephonyProviderId()) {
throw new LogicException(
'Unable to terminate call as we do not yet know the organiser telephony provider id',
);
}
$twilioClient = $this->twilioClientBuilder->build($activity->getTeam());
$twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());
$session->hasTelephonyProviderId()
&& $twilioClient->terminateConference($session->getTelephonyProviderId());
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'session' => $session->id_string,
]);
}
public function answer(VO\ConferenceManager\ActivityAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundAnswer) {
return $this->answerInboundCall($data);
}
if ($data instanceof VO\ConferenceManager\SoftPhoneOutboundAnswer) {
return $this->answerOutboundCall($data);
}
throw new LogicException('Unable to answer - not supported activity answer instance');
}
private function answerInboundCall(VO\ConferenceManager\SoftPhoneInboundAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$dateTime = now();
$activity = $this->create(
new VO\ConferenceManager\SoftPhoneInboundCreate(
$data->getCallerId(),
$data->getCallSid(),
$data->getUser()
)
);
/** @var Models\Participant $activityGuest */
$activityGuest = $activity->from;
$activityGuest
->setEnterTime($dateTime)
->save();
/** @var Connection $connection */
$connection = $activityGuest->connections()
->where('type', Connection::TYPE_PSTN)
->firstOrFail();
$connection
->setTelephonyProviderId($data->getCallSid())
->setOpenedAt($dateTime)
->save();
event(new Events\Activities\Connections\Opened($connection));
return $this->joinTwilioConference($connection, [
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]);
}
private function answerOutboundCall(VO\ConferenceManager\SoftPhoneOutboundAnswer $data): TwiML\VoiceResponse
{
$connection = $data->getConnection();
$participant = $connection->getParticipant();
$activity = $data->getActivity();
$organiser = $activity->organizer;
$dateTime = now();
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
'connection' => $connection->id_string,
'participant' => $participant->id_string,
'activity' => $activity->id_string,
]);
if (! $participant->hasEnterTime()) {
$participant->update([
'enter_time' => $dateTime,
]);
}
$connection->update([
'is_muted' => $participant->isCoach(),
'opened_at' => $dateTime,
]);
event(new Events\Activities\Connections\Opened($connection));
if ($participant->isCoach()) {
/** @var Connection $roomOwnerConnection */
$roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();
return $this->joinTwilioConference(
$connection,
[
'coach' => $roomOwnerConnection->getTelephonyProviderId(),
]
);
}
return $this->joinTwilioConference(
$connection,
[
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]
);
}
private function determineUserCallerId(Models\User $user): string
{
if ($user->hasCallerId()) {
return $user->getCallerId();
}
// TODO: We should require a caller id to be verified before using the dialer
return $user->getSoftPhoneNumber() ?? $user->phone;
}
private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool
{
$startsWith = '[HTTP 404] Unable to create record: The requested resource';
$endsWith = 'was not found';
return Str::startsWith($exceptionMessage, $startsWith)
&& Str::endsWith($exceptionMessage, $endsWith);
}
/**
* @throws RestException
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOtherParty(Connection $callerConnection): self
{
$session = $callerConnection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$this->logger->debug(__METHOD__, [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {
throw new UnexpectedValueException('Unexpected activity type');
}
$callee = $activity->to;
$calleeId = $callee->getPhoneNumber();
/** @var Connection $calleeConnection */
$calleeConnection = $callee->connections()
->whereIn('type', [
Connection::TYPE_PSTN,
Connection::TYPE_WEBRTC,
])
->firstOrFail();
$caller = $activity->from;
// we need to take the number from the connection
// as the one we store on participant level might be the one we call to the rep via PSTN mode
$callerId = $callerConnection->getPhoneNumber();
if (! $caller->is($callerConnection->getParticipant())) {
throw new LogicException(sprintf(
'Unexpected caller participant. Expected: "%s", Actual: "%s"',
$caller->id_string,
$callerConnection->getParticipant()->id_string
));
}
$twilioConferenceSettings = [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
];
$hasInboundRecording = $activity->isTypeSoftphoneInbound()
&& $roomOwner->checkSoftphoneInboundRecordPreference();
$hasOutboundRecording = $activity->isTypeSoftPhone()
&& $roomOwner->checkSoftphoneOutboundRecordPreference();
$isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;
$isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()
|| $activity->hasRecordingReasonComplianceRestricted();
if ($isRecordingEnabled && ! $isRecordingRestricted) {
$twilioConferenceSettings = array_merge($twilioConferenceSettings, [
'record' => true,
'recordingChannels' => 'dual',
'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),
'recordingStatusCallbackMethod' => 'POST',
'recordingStatusCallbackEvent' => 'in-progress completed absent',
]);
if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {
$twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()
? 'outbound'
: 'inbound';
}
}
try {
if ($activity->hasRecordingReasonCompliancePrompted()) {
$this->logger->info('[Softphone] Request compliance prompt via outbound call', [
'activity_uuid' => $activity->getUuid(),
'session' => $session->id_string,
'conference_sid' => $session->getTelephonyProviderId(),
]);
$callInstance = $twilio->createCall(
$callerId,
$calleeId,
$this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [
'session' => $session->id_string,
'connection' => $calleeConnection->id_string,
]),
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $callInstance->sid,
'parent_connection_id' => $callerConnection->getId(),
]);
} else {
$twilioParticipantConnection = $twilio->createParticipantInstance(
$session->getTelephonyProviderId(),
$callerId,
$calleeId,
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $twilioParticipantConnection->callSid,
]);
}
} catch (RestException $restException) {
if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {
throw $restException;
}
if (! $activity->isInFiniteState()) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
$message = __METHOD__ . ': activity.status -> cancelled';
} else {
$message = sprintf(
__METHOD__ . ': not setting to cancelled, activity.status is %s',
$activity->getStatus()
);
}
$this->logger->info($message, [
'activity' => $activity->id_string,
'exception' => $restException,
]);
}
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
return $this;
}
private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array
{
$session = $connection->getSession();
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => false,
'record' => false,
'waitUrl' => '',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]),
'statusCallbackEvent' => [
'initiated',
'ringing',
'answered',
'completed',
],
'statusCallbackMethod' => 'POST',
'conferenceStatusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
]),
'conferenceStatusCallbackMethod' => 'POST',
'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function applyDefaultTwilioConferenceSettings(Models\Session $session, array $override = []): array
{
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => false,
'endConferenceOnExit' => false,
'waitUrl' => '',
'statusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
// 'speaker',
]),
'statusCallbackMethod' => 'POST',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function joinTwilioConference(Connection $connection, array $options = []): TwiML\VoiceResponse
{
$session = $connection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$this->logger->debug(__METHOD__, [
'connection' => $connection->id_string,
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
]);
$voiceResponse = new TwiML\VoiceResponse();
$voiceResponse->dial()
->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]))
->conference(
$session->getIdString(),
$this->applyDefaultTwilioConferenceSettings($session, $options)
);
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'result' => $voiceResponse,
]);
return $voiceResponse;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny_jupiter
Sync Changes
Hide This Notification
Code changed:
Hide
19
19
17
Previous Highlighted Error
Next Highlighted Error
SELECT * FROM teams WHERE id = 1;
select * from crm_layouts where crm_configuration_id = 39;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 283;
SELECT * FROM crm_fields WHERE id = 2234;
SELECT * FROM crm_field_values WHERE crm_field_id = 2234;
select * from crm_profiles where user_id = 143;
select * from record_types where crm_configuration_id = 39; # 0121K000001MHElQAO,0121K000001MHEqQAO
select * from business_processes where crm_configuration_id = 39;
# 01941000000H669AAC, 01941000000H66JAAS
select * from record_type_field_values
where record_type_id IN (24);
select * from crm_field_values where id IN (2730);
select * from crm_configurations where id = 39;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce'; #1035
select * from users where team_id = 1; # 222 group 3
SELECT * FROM activities WHERE user_id = 222 order by id desc;
select * from sidekick_settings where team_id = 1;
select * from teams where id = 1;
select * from team_features where team_id = 1;
select * from activities where crm_configuration_id = 2
and provider = 'ms-teams' and id = 608765;
SELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id = '59523413338';
select * from sidekick_settings where team_id = 2;
SELECT * FROM activities WHERE id = 608660;
select * from activity_summary_logs where activity_id = 608660;
select * from ai_prompts where transcription_id = 11214;
# [PASSWORD_DOTS]
SELECT * FROM activities WHERE uuid_to_bin('ed78a437-2804-450e-ab2f-56ab1c641346') = uuid;
# id: 608818, crm: 59628809737
SELECT * FROM activities WHERE uuid_to_bin('36b06e55-afdd-4782-8dee-c624cd0af191') = uuid;
# id: 608821, crm: 59632069252
SELECT ce.start_time, ce.end_time, a.id, a.uuid, crm_provider_id, calendar_event_id, title,
playbook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id,
scheduled_start_time, scheduled_end_time, actual_start_time, actual_end_time, a.created_at
FROM activities a
join calendar_events ce on a.calendar_event_id = ce.id
WHERE a.id IN (608818, 608821);
select * from users where team_id = 1;
select * from team_settings where team_id = 1;
select * from crm_profiles where crm_configuration_id = 39 order by user_id;
select * from team_features where team_id = 1;
select * from users where team_id = 2;
SELECT * FROM activities WHERE uuid_to_bin('ec7647e9-5225-458b-b475-f31aa2769204') = uuid; # 612639
# Preslava N. Ivanova, grou id 3
SELECT * FROM opportunities WHERE uuid_to_bin('a2928fe5-aec5-46cb-85d9-7654c89e46a6') = uuid;
select * from activities where opportunity_id = 344 and actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00';
select
a.id,
a.type,
a.scheduled_start_time,
a.actual_start_time,
a.created_at,
a.opportunity_id,
a.status
FROM activities a
WHERE opportunity_id = 344
and status IN ('completed', 'received', 'delivered')
and (
(a.actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')
OR (a.created_at between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')
OR (a.scheduled_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00'))
;
SELECT * FROM users WHERE id = 222;
SELECT * FROM crm_profiles WHERE user_id = 222;
select * from crm_layouts where crm_configuration_id = 39;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 281;
select * from group_deal_risk_types;
select * from opportunities where team_id = 1;
SELECT * FROM opportunities WHERE id = 315;
SELECT * FROM crm_field_data WHERE object_id = 315;
select * from crm_field_data where object_id = 260;
select * from generic_ai_prompts where subject_id = 315;
select * from teams; # 36, 21, 121, [EMAIL]
SELECT * FROM social_accounts WHERE sociable_id = 121 and provider = 'bullhorn';
# [PASSWORD_DOTS]
select * from teams where id = 1;
select * from crm_configurations where id = 39;
select * from users where team_id = 1;
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 1;
# 1 - 00541000004281rAAA
# 204 - 0052g000003freeAAA
# 429 - 0052g000003qGOiAAM
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce';
select * from activities where type = 'softphone'
and created_at > '2024-12-11 15:24:36' order by id desc;
select * from activity_providers where team_id = 1;
select * from activity_provider_users where activity_provider_id = 328;
select * from opportunities where crm_configuration_id = 39
AND account_id = 178 AND is_closed = false
order by created_at DESC;
select * from contacts where id = 3952;
select * from accounts where id = 178;
# [PASSWORD_DOTS]
select * from teams where id = 36;
select * from crm_configurations where id = 21;
select * from users where team_id = 36;
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 36;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 36
and sa.provider = 'bullhorn';
select * from social_accounts where id = 348;
UPDATE social_accounts SET
provider_user_token = '21442_6802599_91:41179a58-21e7-4d7c-ad58-56bb666b2f65',
provider_refresh_token = '21442_6802599_91:01c6b335-3f2a-42e4-85ff-8a08fa65fceb',
expires = 1733998131,
state = 'connected'
WHERE id = 348;
# [PASSWORD_DOTS]
select * from teams where id = 31;
select * from crm_configurations where id = 18;
select * from users where team_id = 31; # 257
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 31;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 31
and sa.provider = 'close';
select * from contacts where crm_configuration_id = 18;
# [PASSWORD_DOTS] NEPTUNE [PASSWORD_DOTS]
select * from teams;
select * from users where id IN (1030, 1035, 1052);
select * from crm_configurations;
select * from users where team_id = 65; # 257
select * from team_settings where team_id = 65; # 257
select * from invitations where team_id = 65; # 257
select * from users where email = '[EMAIL]'; # 257
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 65;
select * from crm_configurations where id = 53;
select * from accounts where crm_configuration_id = 53 order by id desc;
select * from leads where crm_configuration_id = 53 order by id desc;
select * from contacts where crm_configuration_id = 53 order by id desc;
select * from opportunities where crm_configuration_id = 53 order by id desc;
select * from crm_profiles where crm_configuration_id = 53 order by id desc;
select * from crm_fields where crm_configuration_id = 53 order by id desc;
select * from crm_field_values where crm_field_id = 3341 order by id desc;
select * from crm_layouts where crm_configuration_id = 53 order by id desc;
select * from stages where crm_configuration_id = 53 order by id desc;
select * from crm_profiles where crm_configuration_id = 13;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 65
and sa.provider = 'integration-app';
select * from contacts where crm_configuration_id = 13;
select * from social_accounts where sociable_id = 283;
SELECT * FROM opportunities WHERE crm_provider_id = '006O400000E9bzeIAB';
select * from activity_providers where team_id = 65;
SELECT * FROM activities WHERE crm_configuration_id IN (51, 52, 53);
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 65
;
# [PASSWORD_DOTS] STAGING [PASSWORD_DOTS]
SELECT * FROM teams;
SELECT * FROM teams WHERE id = 88;
SELECT * FROM teams WHERE id = 89;
select * from team_settings where team_id = 89;
SELECT * FROM users WHERE team_id = 89;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 89;
select * from users;
SELECT * FROM social_accounts WHERE sociable_id = 1761;
SELECT * FROM crm_configurations WHERE id = 70;
select * from accounts where crm_configuration_id = 70 order by id desc;
select * from leads where crm_configuration_id = 70 order by id desc;
select * from contacts where crm_configuration_id = 70 order by id desc;
select * from opportunities where crm_configuration_id = 70 order by id desc;
select * from crm_profiles where crm_configuration_id = 70 order by id desc;
select * from crm_fields where crm_configuration_id = 70 order by id desc;
select * from crm_field_values where crm_field_id = 3536 order by id desc;
select * from crm_layouts where crm_configuration_id = 70 order by id desc;
select * from stages where crm_configuration_id = 70 order by id desc;
select * from business_processes where crm_configuration_id = 70 order by id desc;
select * from business_process_stages where business_process_id = 34;
select * from contacts where id = 10468;
select * from crm_layouts where crm_configuration_id = 70;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 388;
SELECT * FROM crm_fields WHERE id IN (3533,3534,3535);
select * from activities where crm_configuration_id = 70
and (account_id IS NOT NULL or lead_id IS NOT NULL or contact_id IS NOT NULL or opportunity_id IS NOT NULL) order by id desc;
SELECT * FROM activities WHERE uuid_to_bin('2e10b60f-8a61-41c5-a3d4-28835353dc65') = uuid;
SELECT * FROM activities where crm_configuration_id = 69 ;
SELECT * FROM users WHERE email LIKE '%[EMAIL]%';
SELECT * FROM activities WHERE uuid_to_bin('5a150c93-40fc-42ec-b3bd-c1d328e09f6e') = uuid;
SELECT * FROM opportunities WHERE id = 385;
select * from participants p
join activities a on p.activity_id = a.id
where a.crm_configuration_id = 70
and (p.lead_id IS NOT NULL or p.contact_id IS NOT NULL);
SELECT * FROM participants WHERE id = 1013638;
select * from teams where id = 90;
select * from users where team_id = 90;
select * from social_accounts where social_accounts.sociable_id IN (1960,1760);
SELECT * FROM crm_profiles WHERE crm_configuration_id = 71;
select * from invitations where team_id = 90;
select * from crm_configurations where id = 71;
select * from accounts where crm_configuration_id = 71 order by id desc;
select * from leads where crm_configuration_id = 71 order by id desc;
select * from contacts where crm_configuration_id = 71 order by id desc;
select * from opportunities where crm_configuration_id = 71 order by id desc;
select * from crm_profiles where crm_configuration_id = 71 order by id desc;
select * from crm_fields where crm_configuration_id = 71 order by id desc;
select * from crm_field_values where crm_field_id = 3341 order by id desc;
select * from crm_layouts where crm_configuration_id = 71 order by id desc;
select * from stages where crm_configuration_id = 71 order by id desc;
select * from users order by secondary_email desc;
select u.id, u.email, u.status, sa.id, sa.provider_user_id from social_accounts sa
join users u on sa.sociable_id = u.id
where sa.provider = 'google' and u.email LIKE 'aneliya%';
select * from failed_jobs order by id desc;
select * from users where email = '[EMAIL]' or secondary_email = '[EMAIL]';
select * from teams;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 39;
SELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type = 'task';
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce';
# [PASSWORD_DOTS]
SELECT * FROM activities WHERE uuid_to_bin('c38b3895-fd0f-4b1f-9fb2-c170dba137c6') = uuid;
SELECT * FROM crm_configurations WHERE id = 70;
select * from teams where id = 1;
select * from groups where team_id = 1;
select * from users where team_id = 1;
select o.id, o.name,o.close_date, u.id, u.name, u.group_id, r.id, r.display_name, g.name, g.scope from opportunities o
join users u on o.user_id = u.id
join groups g on u.group_id = g.id
join...
|
53084
|
NULL
|
NULL
|
NULL
|
|
53087
|
1861
|
2
|
2026-05-18T11:34:55.649313+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104095649_m2.jpg...
|
PhpStorm
|
faVsco.js – SoftPhoneManager.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\Twilio\Conference\ConferenceManager;
use Illuminate\Database\Eloquent;
use Illuminate\Support\Str;
use Jiminny\Component\ElasticSearch\Model\Observer;
use Jiminny\Component\Twilio\Conference\ConferenceManager;
use Jiminny\Component\Twilio\Exception\OutOfBoundsException;
use Jiminny\Component\Twilio\Exception\UnexpectedValueException;
use Jiminny\Component\Twilio\Resolver;
use Jiminny\Component\Twilio\VO;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Events;
use Jiminny\Exceptions\LogicException;
use Jiminny\Models;
use Jiminny\Models\Participant\Connection;
use Jiminny\Services\ActivityService;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Crm\CachedCrmServiceDecorator;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Services\Telephony\TwilioClientBuilder;
use Psr\Log\LoggerInterface;
use Twilio\Exceptions\ConfigurationException;
use Twilio\Exceptions\RestException;
use Twilio\Exceptions\TwilioException;
use Twilio\TwiML;
final readonly class SoftPhoneManager implements ConferenceManager
{
public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;
public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';
public function __construct(
private ProviderRegistry $crmProviderRegistry,
private Webhook $urlGenerator,
private ActivityService $activityService,
private LoggerInterface $logger,
private TwilioClientBuilder $twilioClientBuilder,
) {
}
public function create(VO\ConferenceManager\ActivityCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'data' => $data,
'instanceof' => get_class($data),
]);
$activity = Observer::skipEvents(function () use ($data) {
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundCreate) {
$activity = $this->createInboundActivity($data);
} elseif ($data instanceof VO\ConferenceManager\SoftPhoneOutboundCreate) {
$activity = $this->createOutboundActivity($data);
} else {
throw new LogicException(sprintf(
'Unknown softphone implementation: %s',
get_class($data)
));
}
return $activity;
});
$this->logger->debug(__METHOD__ . ': done', [
'instanceof' => get_class($data),
'activity' => $activity->id_string,
]);
return $activity;
}
private function createOutboundActivity(VO\ConferenceManager\SoftPhoneOutboundCreate $data): Models\Activity
{
$startTime = microtime(true);
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
$now = now();
$user = $data->getUser();
$team = $user->getTeam();
$device = $data->getDevice();
$phoneNumber = $data->getPhoneNumber();
$route = $data->getPhoneRoute();
if (! $this->activityService->teamCanDial($team, $phoneNumber)) {
$region = getRegionByNumber($phoneNumber, $user->getCountryCode());
throw new LogicException(sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? 'this number'
));
}
$lead = null;
$account = null;
$contact = null;
$stage = null;
$opportunity = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $user,
'providerSlug' => $team->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
} catch (\Throwable $throwable) {
$this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [
'exception' => $throwable,
'user_id' => $user->getId(),
'team_id' => $team->getId(),
]);
$crmService = $this->crmProviderRegistry->get($team->crm->provider);
if (! $user->isCrmRequired()) {
$crmService->setUser($user->team->owner);
} else {
$crmService->setUser($user);
}
}
$prospectId = $data->getProspectId();
try {
if ($prospectId !== null) {
$crmStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [
'prospect_id' => $prospectId,
'object_type' => $data->getObjectType(),
]);
[$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(
$prospectId,
$data->getObjectType()
);
$this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),
'found_lead' => $lead !== null,
'found_account' => $account !== null,
'found_opportunity' => $opportunity !== null,
'found_contact' => $contact !== null,
]);
// Try to attach a contact if we have an opportunity.
if ($opportunity !== null && $contact === null) {
$matchStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');
[, , , $contact, ] = $crmService->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
$this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),
'found_contact' => $contact !== null,
]);
}
} else {
$decorator = resolve(CachedCrmServiceDecorator::class);
$decorator->setCrmService($crmService);
[$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
}
} catch (\Throwable $throwable) {
$this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.
}
if ($data->hasProspectStage()) {
/** @var Models\Stage $stage */
$stage = $team->crm->stages()->uuid($data->getProspectStage());
}
$dbStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting activity creation in DB');
$activity = $user->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE,
'provider' => Models\Activity::PROVIDER_TWILIO,
'device_id' => $device->id ?? null,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'value' => $opportunity ? $opportunity->value : null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_PENDING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),
'scheduled_start_time' => $now,
]);
$this->logger->info('[SoftPhoneManager] Activity created in DB', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),
'activity_id' => $activity->id ?? null,
]);
if (! $activity instanceof Models\Activity) {
throw new LogicException('Activity model expected');
}
$regionId = null;
if ($user->hasRegionId()) {
$regionId = $user->getRegionId();
} elseif ($data->hasRegion()) {
$regionId = $data->getRegion()->getId();
}
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $regionId,
'is_muted' => false,
]);
$phoneRouteResolver = new Resolver\PhoneRouteResolver($user, $route);
$phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();
$connectionType = $phoneRouteResolver->getConnectionType();
$callerId = $this->determineUserCallerId($user);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'user_id' => $user->id,
'email' => $user->email,
'name' => $user->name,
'phone_number' => $phoneRouteResolver->mustUsePSTN()
? $phoneNumberOrigin
: $callerId,
]);
$caller->connections()->create([
'session_id' => $session->getId(),
'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',
'phone' => $callerId,
'type' => $connectionType,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $phoneNumber,
'name' => $activity->prospect_name,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'phone' => $phoneNumber,
'direction' => 'outbound-api',
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$activity->refresh();
$this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [
'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),
'activity_id' => $activity->id_string,
]);
$this->logger->debug(__METHOD__ . ': done', [
'data' => $data,
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
return $activity;
}
private function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$roomOwner = $data->getUser();
$team = $roomOwner->getTeam();
$phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();
$callerId = $data->getCallerId();
$callerCallSid = $data->getCallSid();
$now = now();
if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {
$region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());
throw new LogicException(
sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? ' this number',
),
);
}
$lead = $account = $opportunity = $contact = $stage = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $roomOwner->getTeam(),
'integrationAdmin' => $roomOwner,
'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
[$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(
$callerId,
null,
$roomOwner->getId()
);
} catch (\Throwable $throwable) {
$this->logger->error(__METHOD__ . ': crm exception', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening
}
/** @var Models\Activity $activity */
$activity = $roomOwner->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE_INBOUND,
'provider' => Models\Activity::PROVIDER_TWILIO,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_RINGING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),
'scheduled_start_time' => $now,
]);
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $roomOwner->getRegionId(),
'is_muted' => false,
]);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $callerId,
'name' => $activity->prospect_name,
'enter_time' => $now,
]);
$caller->connections()->create([
'telephony_provider_id' => $callerCallSid,
'session_id' => $session->getId(),
'phone' => $callerId,
'direction' => 'inbound',
'type' => Connection::TYPE_PSTN,
'opened_at' => $now,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'user_id' => $roomOwner->id,
'email' => $roomOwner->email,
'name' => $roomOwner->name,
'phone_number' => $phoneNumber,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'direction' => 'outbound-api',
'phone' => $phoneNumber,
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$this->logger->debug(__METHOD__ . ': done', [
'callSid' => $data->getCallSid(),
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
$activity->refresh();
return $activity;
}
private function getVoiceRecordingReasonCode(Models\Team $team, string $phoneNumber): int
{
$this->logger->debug(__METHOD__, [
'team' => $team->id_string,
'phone' => $phoneNumber,
]);
if (
! $team->hasComplianceModeRecordingPrompt()
&& ! $team->hasComplianceModeRecordingRestrictToOneSide()
&& ! $team->hasComplianceModeRecordingRestrictRecording()
) {
return 0;
}
/** @var Models\VoiceConsentPrefix|null $voiceConsentPolicy */
$voiceConsentPolicy = Models\VoiceConsentPrefix::query()
->whereRaw("? LIKE CONCAT(destination_prefix, '%')", [$phoneNumber])
->where(static function (Eloquent\Builder $builder) use ($team): void {
$builder
->whereNull('team_id')
->orWhere('team_id', $team->getId());
})
->orderByDesc('team_id')
->first();
if (! $voiceConsentPolicy instanceof Models\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {
return 0;
}
if ($team->hasComplianceModeRecordingPrompt()) {
// This number requires consent from the other party before recording the call.
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;
}
if ($team->hasComplianceModeRecordingRestrictToOneSide()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;
}
if ($team->hasComplianceModeRecordingRestrictRecording()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;
}
return 0;
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOrganiser(Models\Activity $activity): void
{
$this->logger->debug(__METHOD__, [
'activity' => $activity->id_string,
]);
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$canBeDialed =
$activity->isTypeSoftPhone()
&& ! $activity->hasStarted()
&& $activity->isPending()
&& $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->exists();
if (! $canBeDialed) {
throw new OutOfBoundsException('Cannot make an outbound call to this activity.');
}
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->firstOrFail();
/** @var Models\Session $session */
$session = $organiserConnection->getSession();
$twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
]);
// What number will the organizer see as caller id
$callerId = $roomOwner->getSoftPhoneNumber();
if ($callerId === null) {
$callee = $activity->participants()->whereNull('user_id')->first();
// This is a bridge call. We make it look like the callee is calling the organizer
$callerId = $callee->phone_number;
}
// What number will the prospect see as caller id
$calleeId = $organiser->getPhoneNumber();
$this->logger->debug(__METHOD__ . ': twilio call', [
'calleeId' => $calleeId,
'callerId' => $callerId,
]);
$twilioParticipant = $twilio->createParticipantInstance(
$session->id_string,
$callerId,
$calleeId,
$twilioConferenceSettings
);
$organiserConnection
->setTelephonyProviderId($twilioParticipant->callSid)
->save();
$session
->setTelephonyProviderId($twilioParticipant->conferenceSid)
->save();
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'session' => $session->id_string,
]);
}
public function dialCoach(Models\Activity $activity, Models\User $user): void
{
throw new \LogicException('Not implemented yet');
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function terminate(VO\ConferenceManager\ActivityTerminate $data): void
{
if (! $data instanceof VO\ConferenceManager\SoftphoneTerminate) {
throw new UnexpectedValueException(sprintf(
'Expecting an instance of "%s", but "%s" given.',
VO\ConferenceManager\SoftphoneTerminate::class,
\get_class($data)
));
}
$this->logger->debug(__METHOD__, [
'activity' => $data->getActivity()->id_string,
'user' => $data->getUser()->id_string,
]);
$activity = $data->getActivity();
$roomOwner = $activity->getUser();
$isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();
if ($isCancelling) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
}
/** @var Models\Session $session */
$session = $activity->sessions->first();
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->whereIn('type', [
Connection::TYPE_WEBRTC,
Connection::TYPE_PSTN,
])
->firstOrFail();
if (! $organiserConnection->hasTelephonyProviderId()) {
throw new LogicException(
'Unable to terminate call as we do not yet know the organiser telephony provider id',
);
}
$twilioClient = $this->twilioClientBuilder->build($activity->getTeam());
$twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());
$session->hasTelephonyProviderId()
&& $twilioClient->terminateConference($session->getTelephonyProviderId());
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'session' => $session->id_string,
]);
}
public function answer(VO\ConferenceManager\ActivityAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundAnswer) {
return $this->answerInboundCall($data);
}
if ($data instanceof VO\ConferenceManager\SoftPhoneOutboundAnswer) {
return $this->answerOutboundCall($data);
}
throw new LogicException('Unable to answer - not supported activity answer instance');
}
private function answerInboundCall(VO\ConferenceManager\SoftPhoneInboundAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$dateTime = now();
$activity = $this->create(
new VO\ConferenceManager\SoftPhoneInboundCreate(
$data->getCallerId(),
$data->getCallSid(),
$data->getUser()
)
);
/** @var Models\Participant $activityGuest */
$activityGuest = $activity->from;
$activityGuest
->setEnterTime($dateTime)
->save();
/** @var Connection $connection */
$connection = $activityGuest->connections()
->where('type', Connection::TYPE_PSTN)
->firstOrFail();
$connection
->setTelephonyProviderId($data->getCallSid())
->setOpenedAt($dateTime)
->save();
event(new Events\Activities\Connections\Opened($connection));
return $this->joinTwilioConference($connection, [
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]);
}
private function answerOutboundCall(VO\ConferenceManager\SoftPhoneOutboundAnswer $data): TwiML\VoiceResponse
{
$connection = $data->getConnection();
$participant = $connection->getParticipant();
$activity = $data->getActivity();
$organiser = $activity->organizer;
$dateTime = now();
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
'connection' => $connection->id_string,
'participant' => $participant->id_string,
'activity' => $activity->id_string,
]);
if (! $participant->hasEnterTime()) {
$participant->update([
'enter_time' => $dateTime,
]);
}
$connection->update([
'is_muted' => $participant->isCoach(),
'opened_at' => $dateTime,
]);
event(new Events\Activities\Connections\Opened($connection));
if ($participant->isCoach()) {
/** @var Connection $roomOwnerConnection */
$roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();
return $this->joinTwilioConference(
$connection,
[
'coach' => $roomOwnerConnection->getTelephonyProviderId(),
]
);
}
return $this->joinTwilioConference(
$connection,
[
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]
);
}
private function determineUserCallerId(Models\User $user): string
{
if ($user->hasCallerId()) {
return $user->getCallerId();
}
// TODO: We should require a caller id to be verified before using the dialer
return $user->getSoftPhoneNumber() ?? $user->phone;
}
private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool
{
$startsWith = '[HTTP 404] Unable to create record: The requested resource';
$endsWith = 'was not found';
return Str::startsWith($exceptionMessage, $startsWith)
&& Str::endsWith($exceptionMessage, $endsWith);
}
/**
* @throws RestException
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOtherParty(Connection $callerConnection): self
{
$session = $callerConnection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$this->logger->debug(__METHOD__, [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {
throw new UnexpectedValueException('Unexpected activity type');
}
$callee = $activity->to;
$calleeId = $callee->getPhoneNumber();
/** @var Connection $calleeConnection */
$calleeConnection = $callee->connections()
->whereIn('type', [
Connection::TYPE_PSTN,
Connection::TYPE_WEBRTC,
])
->firstOrFail();
$caller = $activity->from;
// we need to take the number from the connection
// as the one we store on participant level might be the one we call to the rep via PSTN mode
$callerId = $callerConnection->getPhoneNumber();
if (! $caller->is($callerConnection->getParticipant())) {
throw new LogicException(sprintf(
'Unexpected caller participant. Expected: "%s", Actual: "%s"',
$caller->id_string,
$callerConnection->getParticipant()->id_string
));
}
$twilioConferenceSettings = [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
];
$hasInboundRecording = $activity->isTypeSoftphoneInbound()
&& $roomOwner->checkSoftphoneInboundRecordPreference();
$hasOutboundRecording = $activity->isTypeSoftPhone()
&& $roomOwner->checkSoftphoneOutboundRecordPreference();
$isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;
$isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()
|| $activity->hasRecordingReasonComplianceRestricted();
if ($isRecordingEnabled && ! $isRecordingRestricted) {
$twilioConferenceSettings = array_merge($twilioConferenceSettings, [
'record' => true,
'recordingChannels' => 'dual',
'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),
'recordingStatusCallbackMethod' => 'POST',
'recordingStatusCallbackEvent' => 'in-progress completed absent',
]);
if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {
$twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()
? 'outbound'
: 'inbound';
}
}
try {
if ($activity->hasRecordingReasonCompliancePrompted()) {
$this->logger->info('[Softphone] Request compliance prompt via outbound call', [
'activity_uuid' => $activity->getUuid(),
'session' => $session->id_string,
'conference_sid' => $session->getTelephonyProviderId(),
]);
$callInstance = $twilio->createCall(
$callerId,
$calleeId,
$this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [
'session' => $session->id_string,
'connection' => $calleeConnection->id_string,
]),
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $callInstance->sid,
'parent_connection_id' => $callerConnection->getId(),
]);
} else {
$twilioParticipantConnection = $twilio->createParticipantInstance(
$session->getTelephonyProviderId(),
$callerId,
$calleeId,
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $twilioParticipantConnection->callSid,
]);
}
} catch (RestException $restException) {
if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {
throw $restException;
}
if (! $activity->isInFiniteState()) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
$message = __METHOD__ . ': activity.status -> cancelled';
} else {
$message = sprintf(
__METHOD__ . ': not setting to cancelled, activity.status is %s',
$activity->getStatus()
);
}
$this->logger->info($message, [
'activity' => $activity->id_string,
'exception' => $restException,
]);
}
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
return $this;
}
private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array
{
$session = $connection->getSession();
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => false,
'record' => false,
'waitUrl' => '',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]),
'statusCallbackEvent' => [
'initiated',
'ringing',
'answered',
'completed',
],
'statusCallbackMethod' => 'POST',
'conferenceStatusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
]),
'conferenceStatusCallbackMethod' => 'POST',
'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function applyDefaultTwilioConferenceSettings(Models\Session $session, array $override = []): array
{
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => false,
'endConferenceOnExit' => false,
'waitUrl' => '',
'statusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
// 'speaker',
]),
'statusCallbackMethod' => 'POST',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function joinTwilioConference(Connection $connection, array $options = []): TwiML\VoiceResponse
{
$session = $connection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$this->logger->debug(__METHOD__, [
'connection' => $connection->id_string,
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
]);
$voiceResponse = new TwiML\VoiceResponse();
$voiceResponse->dial()
->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]))
->conference(
$session->getIdString(),
$this->applyDefaultTwilioConferenceSettings($session, $options)
);
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'result' => $voiceResponse,
]);
return $voiceResponse;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny_jupiter
Sync Changes
Hide This Notification
Code changed:
Hide
19
19
17
Previous Highlighted Error
Next Highlighted Error
SELECT * FROM teams WHERE id = 1;
select * from crm_layouts where crm_configuration_id = 39;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 283;
SELECT * FROM crm_fields WHERE id = 2234;
SELECT * FROM crm_field_values WHERE crm_field_id = 2234;
select * from crm_profiles where user_id = 143;
select * from record_types where crm_configuration_id = 39; # 0121K000001MHElQAO,0121K000001MHEqQAO
select * from business_processes where crm_configuration_id = 39;
# 01941000000H669AAC, 01941000000H66JAAS
select * from record_type_field_values
where record_type_id IN (24);
select * from crm_field_values where id IN (2730);
select * from crm_configurations where id = 39;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce'; #1035
select * from users where team_id = 1; # 222 group 3
SELECT * FROM activities WHERE user_id = 222 order by id desc;
select * from sidekick_settings where team_id = 1;
select * from teams where id = 1;
select * from team_features where team_id = 1;
select * from activities where crm_configuration_id = 2
and provider = 'ms-teams' and id = 608765;
SELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id = '59523413338';
select * from sidekick_settings where team_id = 2;
SELECT * FROM activities WHERE id = 608660;
select * from activity_summary_logs where activity_id = 608660;
select * from ai_prompts where transcription_id = 11214;
# [PASSWORD_DOTS]
SELECT * FROM activities WHERE uuid_to_bin('ed78a437-2804-450e-ab2f-56ab1c641346') = uuid;
# id: 608818, crm: 59628809737
SELECT * FROM activities WHERE uuid_to_bin('36b06e55-afdd-4782-8dee-c624cd0af191') = uuid;
# id: 608821, crm: 59632069252
SELECT ce.start_time, ce.end_time, a.id, a.uuid, crm_provider_id, calendar_event_id, title,
playbook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id,
scheduled_start_time, scheduled_end_time, actual_start_time, actual_end_time, a.created_at
FROM activities a
join calendar_events ce on a.calendar_event_id = ce.id
WHERE a.id IN (608818, 608821);
select * from users where team_id = 1;
select * from team_settings where team_id = 1;
select * from crm_profiles where crm_configuration_id = 39 order by user_id;
select * from team_features where team_id = 1;
select * from users where team_id = 2;
SELECT * FROM activities WHERE uuid_to_bin('ec7647e9-5225-458b-b475-f31aa2769204') = uuid; # 612639
# Preslava N. Ivanova, grou id 3
SELECT * FROM opportunities WHERE uuid_to_bin('a2928fe5-aec5-46cb-85d9-7654c89e46a6') = uuid;
select * from activities where opportunity_id = 344 and actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00';
select
a.id,
a.type,
a.scheduled_start_time,
a.actual_start_time,
a.created_at,
a.opportunity_id,
a.status
FROM activities a
WHERE opportunity_id = 344
and status IN ('completed', 'received', 'delivered')
and (
(a.actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')
OR (a.created_at between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')
OR (a.scheduled_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00'))
;
SELECT * FROM users WHERE id = 222;
SELECT * FROM crm_profiles WHERE user_id = 222;
select * from crm_layouts where crm_configuration_id = 39;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 281;
select * from group_deal_risk_types;
select * from opportunities where team_id = 1;
SELECT * FROM opportunities WHERE id = 315;
SELECT * FROM crm_field_data WHERE object_id = 315;
select * from crm_field_data where object_id = 260;
select * from generic_ai_prompts where subject_id = 315;
select * from teams; # 36, 21, 121, [EMAIL]
SELECT * FROM social_accounts WHERE sociable_id = 121 and provider = 'bullhorn';
# [PASSWORD_DOTS]
select * from teams where id = 1;
select * from crm_configurations where id = 39;
select * from users where team_id = 1;
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 1;
# 1 - 00541000004281rAAA
# 204 - 0052g000003freeAAA
# 429 - 0052g000003qGOiAAM
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce';
select * from activities where type = 'softphone'
and created_at > '2024-12-11 15:24:36' order by id desc;
select * from activity_providers where team_id = 1;
select * from activity_provider_users where activity_provider_id = 328;
select * from opportunities where crm_configuration_id = 39
AND account_id = 178 AND is_closed = false
order by created_at DESC;
select * from contacts where id = 3952;
select * from accounts where id = 178;
# [PASSWORD_DOTS]
select * from teams where id = 36;
select * from crm_configurations where id = 21;
select * from users where team_id = 36;
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 36;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 36
and sa.provider = 'bullhorn';
select * from social_accounts where id = 348;
UPDATE social_accounts SET
provider_user_token = '21442_6802599_91:41179a58-21e7-4d7c-ad58-56bb666b2f65',
provider_refresh_token = '21442_6802599_91:01c6b335-3f2a-42e4-85ff-8a08fa65fceb',
expires = 1733998131,
state = 'connected'
WHERE id = 348;
# [PASSWORD_DOTS]
select * from teams where id = 31;
select * from crm_configurations where id = 18;
select * from users where team_id = 31; # 257
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 31;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 31
and sa.provider = 'close';
select * from contacts where crm_configuration_id = 18;
# [PASSWORD_DOTS] NEPTUNE [PASSWORD_DOTS]
select * from teams;
select * from users where id IN (1030, 1035, 1052);
select * from crm_configurations;
select * from users where team_id = 65; # 257
select * from team_settings where team_id = 65; # 257
select * from invitations where team_id = 65; # 257
select * from users where email = '[EMAIL]'; # 257
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 65;
select * from crm_configurations where id = 53;
select * from accounts where crm_configuration_id = 53 order by id desc;
select * from leads where crm_configuration_id = 53 order by id desc;
select * from contacts where crm_configuration_id = 53 order by id desc;
select * from opportunities where crm_configuration_id = 53 order by id desc;
select * from crm_profiles where crm_configuration_id = 53 order by id desc;
select * from crm_fields where crm_configuration_id = 53 order by id desc;
select * from crm_field_values where crm_field_id = 3341 order by id desc;
select * from crm_layouts where crm_configuration_id = 53 order by id desc;
select * from stages where crm_configuration_id = 53 order by id desc;
select * from crm_profiles where crm_configuration_id = 13;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 65
and sa.provider = 'integration-app';
select * from contacts where crm_configuration_id = 13;
select * from social_accounts where sociable_id = 283;
SELECT * FROM opportunities WHERE crm_provider_id = '006O400000E9bzeIAB';
select * from activity_providers where team_id = 65;
SELECT * FROM activities WHERE crm_configuration_id IN (51, 52, 53);
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 65
;
# [PASSWORD_DOTS] STAGING [PASSWORD_DOTS]
SELECT * FROM teams;
SELECT * FROM teams WHERE id = 88;
SELECT * FROM teams WHERE id = 89;
select * from team_settings where team_id = 89;
SELECT * FROM users WHERE team_id = 89;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 89;
select * from users;
SELECT * FROM social_accounts WHERE sociable_id = 1761;
SELECT * FROM crm_configurations WHERE id = 70;
select * from accounts where crm_configuration_id = 70 order by id desc;
select * from leads where crm_configuration_id = 70 order by id desc;
select * from contacts where crm_configuration_id = 70 order by id desc;
select * from opportunities where crm_configuration_id = 70 order by id desc;
select * from crm_profiles where crm_configuration_id = 70 order by id desc;
select * from crm_fields where crm_configuration_id = 70 order by id desc;
select * from crm_field_values where crm_field_id = 3536 order by id desc;
select * from crm_layouts where crm_configuration_id = 70 order by id desc;
select * from stages where crm_configuration_id = 70 order by id desc;
select * from business_processes where crm_configuration_id = 70 order by id desc;
select * from business_process_stages where business_process_id = 34;
select * from contacts where id = 10468;
select * from crm_layouts where crm_configuration_id = 70;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 388;
SELECT * FROM crm_fields WHERE id IN (3533,3534,3535);
select * from activities where crm_configuration_id = 70
and (account_id IS NOT NULL or lead_id IS NOT NULL or contact_id IS NOT NULL or opportunity_id IS NOT NULL) order by id desc;
SELECT * FROM activities WHERE uuid_to_bin('2e10b60f-8a61-41c5-a3d4-28835353dc65') = uuid;
SELECT * FROM activities where crm_configuration_id = 69 ;
SELECT * FROM users WHERE email LIKE '%[EMAIL]%';
SELECT * FROM activities WHERE uuid_to_bin('5a150c93-40fc-42ec-b3bd-c1d328e09f6e') = uuid;
SELECT * FROM opportunities WHERE id = 385;
select * from participants p
join activities a on p.activity_id = a.id
where a.crm_configuration_id = 70
and (p.lead_id IS NOT NULL or p.contact_id IS NOT NULL);
SELECT * FROM participants WHERE id = 1013638;
select * from teams where id = 90;
select * from users where team_id = 90;
select * from social_accounts where social_accounts.sociable_id IN (1960,1760);
SELECT * FROM crm_profiles WHERE crm_configuration_id = 71;
select * from invitations where team_id = 90;
select * from crm_configurations where id = 71;
select * from accounts where crm_configuration_id = 71 order by id desc;
select * from leads where crm_configuration_id = 71 order by id desc;
select * from contacts where crm_configuration_id = 71 order by id desc;
select * from opportunities where crm_configuration_id = 71 order by id desc;
select * from crm_profiles where crm_configuration_id = 71 order by id desc;
select * from crm_fields where crm_configuration_id = 71 order by id desc;
select * from crm_field_values where crm_field_id = 3341 order by id desc;
select * from crm_layouts where crm_configuration_id = 71 order by id desc;
select * from stages where crm_configuration_id = 71 order by id desc;
select * from users order by secondary_email desc;
select u.id, u.email, u.status, sa.id, sa.provider_user_id from social_accounts sa
join users u on sa.sociable_id = u.id
where sa.provider = 'google' and u.email LIKE 'aneliya%';
select * from failed_jobs order by id desc;
select * from users where email = '[EMAIL]' or secondary_email = '[EMAIL]';
select * from teams;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 39;
SELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type = 'task';
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce';
# [PASSWORD_DOTS]
SELECT * FROM activities WHERE uuid_to_bin('c38b3895-fd0f-4b1f-9fb2-c170dba137c6') = uuid;
SELECT * FROM crm_configurations WHERE id = 70;
select * from teams where id = 1;
select * from groups where team_id = 1;
select * from users where team_id = 1;
select o.id, o.name,o.close_date, u.id, u.name, u.group_id, r.id, r.display_name, g.name, g.scope from opportunities o
join users u on o.user_id = u.id
join groups g on u.group_id = g.id
join...
|
[{"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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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":"9","depth":4,"bounds":{"left":0.3929521,"top":0.10055866,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.40292552,"top":0.10055866,"width":0.00731383,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.4119016,"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.4192154,"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\\Component\\Twilio\\Conference\\ConferenceManager;\n\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Support\\Str;\nuse Jiminny\\Component\\ElasticSearch\\Model\\Observer;\nuse Jiminny\\Component\\Twilio\\Conference\\ConferenceManager;\nuse Jiminny\\Component\\Twilio\\Exception\\OutOfBoundsException;\nuse Jiminny\\Component\\Twilio\\Exception\\UnexpectedValueException;\nuse Jiminny\\Component\\Twilio\\Resolver;\nuse Jiminny\\Component\\Twilio\\VO;\nuse Jiminny\\Component\\UrlGenerator\\Webhook;\nuse Jiminny\\Events;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Services\\ActivityService;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Crm\\CachedCrmServiceDecorator;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Services\\Telephony\\TwilioClientBuilder;\nuse Psr\\Log\\LoggerInterface;\nuse Twilio\\Exceptions\\ConfigurationException;\nuse Twilio\\Exceptions\\RestException;\nuse Twilio\\Exceptions\\TwilioException;\nuse Twilio\\TwiML;\n\nfinal readonly class SoftPhoneManager implements ConferenceManager\n{\n public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;\n public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';\n\n public function __construct(\n private ProviderRegistry $crmProviderRegistry,\n private Webhook $urlGenerator,\n private ActivityService $activityService,\n private LoggerInterface $logger,\n private TwilioClientBuilder $twilioClientBuilder,\n ) {\n }\n\n public function create(VO\\ConferenceManager\\ActivityCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n 'instanceof' => get_class($data),\n ]);\n\n $activity = Observer::skipEvents(function () use ($data) {\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundCreate) {\n $activity = $this->createInboundActivity($data);\n } elseif ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundCreate) {\n $activity = $this->createOutboundActivity($data);\n } else {\n throw new LogicException(sprintf(\n 'Unknown softphone implementation: %s',\n get_class($data)\n ));\n }\n\n return $activity;\n });\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'instanceof' => get_class($data),\n 'activity' => $activity->id_string,\n ]);\n\n return $activity;\n }\n\n private function createOutboundActivity(VO\\ConferenceManager\\SoftPhoneOutboundCreate $data): Models\\Activity\n {\n $startTime = microtime(true);\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n $now = now();\n $user = $data->getUser();\n $team = $user->getTeam();\n $device = $data->getDevice();\n $phoneNumber = $data->getPhoneNumber();\n $route = $data->getPhoneRoute();\n\n if (! $this->activityService->teamCanDial($team, $phoneNumber)) {\n $region = getRegionByNumber($phoneNumber, $user->getCountryCode());\n\n throw new LogicException(sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? 'this number'\n ));\n }\n\n $lead = null;\n $account = null;\n $contact = null;\n $stage = null;\n $opportunity = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $user,\n 'providerSlug' => $team->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n } catch (\\Throwable $throwable) {\n $this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [\n 'exception' => $throwable,\n 'user_id' => $user->getId(),\n 'team_id' => $team->getId(),\n ]);\n $crmService = $this->crmProviderRegistry->get($team->crm->provider);\n if (! $user->isCrmRequired()) {\n $crmService->setUser($user->team->owner);\n } else {\n $crmService->setUser($user);\n }\n }\n\n $prospectId = $data->getProspectId();\n\n try {\n if ($prospectId !== null) {\n $crmStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [\n 'prospect_id' => $prospectId,\n 'object_type' => $data->getObjectType(),\n ]);\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(\n $prospectId,\n $data->getObjectType()\n );\n $this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),\n 'found_lead' => $lead !== null,\n 'found_account' => $account !== null,\n 'found_opportunity' => $opportunity !== null,\n 'found_contact' => $contact !== null,\n ]);\n\n // Try to attach a contact if we have an opportunity.\n if ($opportunity !== null && $contact === null) {\n $matchStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');\n [, , , $contact, ] = $crmService->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n $this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),\n 'found_contact' => $contact !== null,\n ]);\n }\n } else {\n $decorator = resolve(CachedCrmServiceDecorator::class);\n $decorator->setCrmService($crmService);\n\n [$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n }\n } catch (\\Throwable $throwable) {\n $this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.\n }\n\n if ($data->hasProspectStage()) {\n /** @var Models\\Stage $stage */\n $stage = $team->crm->stages()->uuid($data->getProspectStage());\n }\n\n $dbStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting activity creation in DB');\n $activity = $user->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'device_id' => $device->id ?? null,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'value' => $opportunity ? $opportunity->value : null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_PENDING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),\n 'scheduled_start_time' => $now,\n ]);\n $this->logger->info('[SoftPhoneManager] Activity created in DB', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),\n 'activity_id' => $activity->id ?? null,\n ]);\n\n if (! $activity instanceof Models\\Activity) {\n throw new LogicException('Activity model expected');\n }\n\n $regionId = null;\n if ($user->hasRegionId()) {\n $regionId = $user->getRegionId();\n } elseif ($data->hasRegion()) {\n $regionId = $data->getRegion()->getId();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $regionId,\n 'is_muted' => false,\n ]);\n\n $phoneRouteResolver = new Resolver\\PhoneRouteResolver($user, $route);\n $phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();\n $connectionType = $phoneRouteResolver->getConnectionType();\n $callerId = $this->determineUserCallerId($user);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'user_id' => $user->id,\n 'email' => $user->email,\n 'name' => $user->name,\n 'phone_number' => $phoneRouteResolver->mustUsePSTN()\n ? $phoneNumberOrigin\n : $callerId,\n ]);\n\n $caller->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',\n 'phone' => $callerId,\n 'type' => $connectionType,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $phoneNumber,\n 'name' => $activity->prospect_name,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'phone' => $phoneNumber,\n 'direction' => 'outbound-api',\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $activity->refresh();\n\n $this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [\n 'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'activity_id' => $activity->id_string,\n ]);\n $this->logger->debug(__METHOD__ . ': done', [\n 'data' => $data,\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n return $activity;\n }\n\n private function createInboundActivity(VO\\ConferenceManager\\SoftPhoneInboundCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $roomOwner = $data->getUser();\n $team = $roomOwner->getTeam();\n $phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();\n $callerId = $data->getCallerId();\n $callerCallSid = $data->getCallSid();\n\n $now = now();\n\n if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {\n $region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());\n\n throw new LogicException(\n sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? ' this number',\n ),\n );\n }\n\n $lead = $account = $opportunity = $contact = $stage = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $roomOwner->getTeam(),\n 'integrationAdmin' => $roomOwner,\n 'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(\n $callerId,\n null,\n $roomOwner->getId()\n );\n } catch (\\Throwable $throwable) {\n $this->logger->error(__METHOD__ . ': crm exception', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening\n }\n\n /** @var Models\\Activity $activity */\n $activity = $roomOwner->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE_INBOUND,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_RINGING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),\n 'scheduled_start_time' => $now,\n ]);\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $roomOwner->getRegionId(),\n 'is_muted' => false,\n ]);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $callerId,\n 'name' => $activity->prospect_name,\n 'enter_time' => $now,\n ]);\n\n $caller->connections()->create([\n 'telephony_provider_id' => $callerCallSid,\n 'session_id' => $session->getId(),\n 'phone' => $callerId,\n 'direction' => 'inbound',\n 'type' => Connection::TYPE_PSTN,\n 'opened_at' => $now,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'user_id' => $roomOwner->id,\n 'email' => $roomOwner->email,\n 'name' => $roomOwner->name,\n 'phone_number' => $phoneNumber,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => 'outbound-api',\n 'phone' => $phoneNumber,\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'callSid' => $data->getCallSid(),\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n $activity->refresh();\n\n return $activity;\n }\n\n private function getVoiceRecordingReasonCode(Models\\Team $team, string $phoneNumber): int\n {\n $this->logger->debug(__METHOD__, [\n 'team' => $team->id_string,\n 'phone' => $phoneNumber,\n ]);\n\n if (\n ! $team->hasComplianceModeRecordingPrompt()\n && ! $team->hasComplianceModeRecordingRestrictToOneSide()\n && ! $team->hasComplianceModeRecordingRestrictRecording()\n ) {\n return 0;\n }\n\n /** @var Models\\VoiceConsentPrefix|null $voiceConsentPolicy */\n $voiceConsentPolicy = Models\\VoiceConsentPrefix::query()\n ->whereRaw(\"? LIKE CONCAT(destination_prefix, '%')\", [$phoneNumber])\n ->where(static function (Eloquent\\Builder $builder) use ($team): void {\n $builder\n ->whereNull('team_id')\n ->orWhere('team_id', $team->getId());\n })\n ->orderByDesc('team_id')\n ->first();\n\n if (! $voiceConsentPolicy instanceof Models\\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {\n return 0;\n }\n\n if ($team->hasComplianceModeRecordingPrompt()) {\n // This number requires consent from the other party before recording the call.\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;\n }\n\n if ($team->hasComplianceModeRecordingRestrictToOneSide()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;\n }\n\n if ($team->hasComplianceModeRecordingRestrictRecording()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;\n }\n\n return 0;\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOrganiser(Models\\Activity $activity): void\n {\n $this->logger->debug(__METHOD__, [\n 'activity' => $activity->id_string,\n ]);\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $canBeDialed =\n $activity->isTypeSoftPhone()\n && ! $activity->hasStarted()\n && $activity->isPending()\n && $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->exists();\n\n if (! $canBeDialed) {\n throw new OutOfBoundsException('Cannot make an outbound call to this activity.');\n }\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->firstOrFail();\n\n /** @var Models\\Session $session */\n $session = $organiserConnection->getSession();\n\n $twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ]);\n\n // What number will the organizer see as caller id\n $callerId = $roomOwner->getSoftPhoneNumber();\n\n if ($callerId === null) {\n $callee = $activity->participants()->whereNull('user_id')->first();\n // This is a bridge call. We make it look like the callee is calling the organizer\n $callerId = $callee->phone_number;\n }\n\n // What number will the prospect see as caller id\n $calleeId = $organiser->getPhoneNumber();\n\n $this->logger->debug(__METHOD__ . ': twilio call', [\n 'calleeId' => $calleeId,\n 'callerId' => $callerId,\n ]);\n\n $twilioParticipant = $twilio->createParticipantInstance(\n $session->id_string,\n $callerId,\n $calleeId,\n $twilioConferenceSettings\n );\n\n $organiserConnection\n ->setTelephonyProviderId($twilioParticipant->callSid)\n ->save();\n\n $session\n ->setTelephonyProviderId($twilioParticipant->conferenceSid)\n ->save();\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function dialCoach(Models\\Activity $activity, Models\\User $user): void\n {\n throw new \\LogicException('Not implemented yet');\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function terminate(VO\\ConferenceManager\\ActivityTerminate $data): void\n {\n if (! $data instanceof VO\\ConferenceManager\\SoftphoneTerminate) {\n throw new UnexpectedValueException(sprintf(\n 'Expecting an instance of \"%s\", but \"%s\" given.',\n VO\\ConferenceManager\\SoftphoneTerminate::class,\n \\get_class($data)\n ));\n }\n\n $this->logger->debug(__METHOD__, [\n 'activity' => $data->getActivity()->id_string,\n 'user' => $data->getUser()->id_string,\n ]);\n\n $activity = $data->getActivity();\n $roomOwner = $activity->getUser();\n\n $isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();\n\n if ($isCancelling) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions->first();\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->whereIn('type', [\n Connection::TYPE_WEBRTC,\n Connection::TYPE_PSTN,\n ])\n ->firstOrFail();\n\n if (! $organiserConnection->hasTelephonyProviderId()) {\n throw new LogicException(\n 'Unable to terminate call as we do not yet know the organiser telephony provider id',\n );\n }\n\n $twilioClient = $this->twilioClientBuilder->build($activity->getTeam());\n\n $twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());\n\n $session->hasTelephonyProviderId()\n && $twilioClient->terminateConference($session->getTelephonyProviderId());\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function answer(VO\\ConferenceManager\\ActivityAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundAnswer) {\n return $this->answerInboundCall($data);\n }\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundAnswer) {\n return $this->answerOutboundCall($data);\n }\n\n throw new LogicException('Unable to answer - not supported activity answer instance');\n }\n\n private function answerInboundCall(VO\\ConferenceManager\\SoftPhoneInboundAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $dateTime = now();\n\n $activity = $this->create(\n new VO\\ConferenceManager\\SoftPhoneInboundCreate(\n $data->getCallerId(),\n $data->getCallSid(),\n $data->getUser()\n )\n );\n\n /** @var Models\\Participant $activityGuest */\n $activityGuest = $activity->from;\n\n $activityGuest\n ->setEnterTime($dateTime)\n ->save();\n\n /** @var Connection $connection */\n $connection = $activityGuest->connections()\n ->where('type', Connection::TYPE_PSTN)\n ->firstOrFail();\n\n $connection\n ->setTelephonyProviderId($data->getCallSid())\n ->setOpenedAt($dateTime)\n ->save();\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n return $this->joinTwilioConference($connection, [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]);\n }\n\n private function answerOutboundCall(VO\\ConferenceManager\\SoftPhoneOutboundAnswer $data): TwiML\\VoiceResponse\n {\n $connection = $data->getConnection();\n $participant = $connection->getParticipant();\n $activity = $data->getActivity();\n $organiser = $activity->organizer;\n $dateTime = now();\n\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n 'connection' => $connection->id_string,\n 'participant' => $participant->id_string,\n 'activity' => $activity->id_string,\n ]);\n\n if (! $participant->hasEnterTime()) {\n $participant->update([\n 'enter_time' => $dateTime,\n ]);\n }\n\n $connection->update([\n 'is_muted' => $participant->isCoach(),\n 'opened_at' => $dateTime,\n ]);\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n if ($participant->isCoach()) {\n /** @var Connection $roomOwnerConnection */\n $roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'coach' => $roomOwnerConnection->getTelephonyProviderId(),\n ]\n );\n }\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]\n );\n }\n\n private function determineUserCallerId(Models\\User $user): string\n {\n if ($user->hasCallerId()) {\n return $user->getCallerId();\n }\n\n // TODO: We should require a caller id to be verified before using the dialer\n return $user->getSoftPhoneNumber() ?? $user->phone;\n }\n\n private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool\n {\n $startsWith = '[HTTP 404] Unable to create record: The requested resource';\n $endsWith = 'was not found';\n\n return Str::startsWith($exceptionMessage, $startsWith)\n && Str::endsWith($exceptionMessage, $endsWith);\n }\n\n /**\n * @throws RestException\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOtherParty(Connection $callerConnection): self\n {\n $session = $callerConnection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $this->logger->debug(__METHOD__, [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {\n throw new UnexpectedValueException('Unexpected activity type');\n }\n\n $callee = $activity->to;\n $calleeId = $callee->getPhoneNumber();\n\n /** @var Connection $calleeConnection */\n $calleeConnection = $callee->connections()\n ->whereIn('type', [\n Connection::TYPE_PSTN,\n Connection::TYPE_WEBRTC,\n ])\n ->firstOrFail();\n\n $caller = $activity->from;\n\n // we need to take the number from the connection\n // as the one we store on participant level might be the one we call to the rep via PSTN mode\n $callerId = $callerConnection->getPhoneNumber();\n\n if (! $caller->is($callerConnection->getParticipant())) {\n throw new LogicException(sprintf(\n 'Unexpected caller participant. Expected: \"%s\", Actual: \"%s\"',\n $caller->id_string,\n $callerConnection->getParticipant()->id_string\n ));\n }\n\n $twilioConferenceSettings = [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ];\n\n $hasInboundRecording = $activity->isTypeSoftphoneInbound()\n && $roomOwner->checkSoftphoneInboundRecordPreference();\n\n $hasOutboundRecording = $activity->isTypeSoftPhone()\n && $roomOwner->checkSoftphoneOutboundRecordPreference();\n\n $isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;\n\n $isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()\n || $activity->hasRecordingReasonComplianceRestricted();\n\n if ($isRecordingEnabled && ! $isRecordingRestricted) {\n $twilioConferenceSettings = array_merge($twilioConferenceSettings, [\n 'record' => true,\n 'recordingChannels' => 'dual',\n 'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),\n 'recordingStatusCallbackMethod' => 'POST',\n 'recordingStatusCallbackEvent' => 'in-progress completed absent',\n ]);\n\n if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n $twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()\n ? 'outbound'\n : 'inbound';\n }\n }\n\n try {\n if ($activity->hasRecordingReasonCompliancePrompted()) {\n $this->logger->info('[Softphone] Request compliance prompt via outbound call', [\n 'activity_uuid' => $activity->getUuid(),\n 'session' => $session->id_string,\n 'conference_sid' => $session->getTelephonyProviderId(),\n ]);\n\n $callInstance = $twilio->createCall(\n $callerId,\n $calleeId,\n $this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [\n 'session' => $session->id_string,\n 'connection' => $calleeConnection->id_string,\n ]),\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $callInstance->sid,\n 'parent_connection_id' => $callerConnection->getId(),\n ]);\n } else {\n $twilioParticipantConnection = $twilio->createParticipantInstance(\n $session->getTelephonyProviderId(),\n $callerId,\n $calleeId,\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $twilioParticipantConnection->callSid,\n ]);\n }\n } catch (RestException $restException) {\n if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {\n throw $restException;\n }\n\n if (! $activity->isInFiniteState()) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n\n $message = __METHOD__ . ': activity.status -> cancelled';\n } else {\n $message = sprintf(\n __METHOD__ . ': not setting to cancelled, activity.status is %s',\n $activity->getStatus()\n );\n }\n\n $this->logger->info($message, [\n 'activity' => $activity->id_string,\n 'exception' => $restException,\n ]);\n }\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n return $this;\n }\n\n private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array\n {\n $session = $connection->getSession();\n\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => false,\n 'record' => false,\n 'waitUrl' => '',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]),\n 'statusCallbackEvent' => [\n 'initiated',\n 'ringing',\n 'answered',\n 'completed',\n ],\n 'statusCallbackMethod' => 'POST',\n 'conferenceStatusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n ]),\n 'conferenceStatusCallbackMethod' => 'POST',\n 'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function applyDefaultTwilioConferenceSettings(Models\\Session $session, array $override = []): array\n {\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => false,\n 'endConferenceOnExit' => false,\n 'waitUrl' => '',\n 'statusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n// 'speaker',\n ]),\n 'statusCallbackMethod' => 'POST',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function joinTwilioConference(Connection $connection, array $options = []): TwiML\\VoiceResponse\n {\n $session = $connection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n\n $this->logger->debug(__METHOD__, [\n 'connection' => $connection->id_string,\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n ]);\n\n $voiceResponse = new TwiML\\VoiceResponse();\n\n $voiceResponse->dial()\n ->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]))\n ->conference(\n $session->getIdString(),\n $this->applyDefaultTwilioConferenceSettings($session, $options)\n );\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'result' => $voiceResponse,\n ]);\n\n return $voiceResponse;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\Twilio\\Conference\\ConferenceManager;\n\nuse Illuminate\\Database\\Eloquent;\nuse Illuminate\\Support\\Str;\nuse Jiminny\\Component\\ElasticSearch\\Model\\Observer;\nuse Jiminny\\Component\\Twilio\\Conference\\ConferenceManager;\nuse Jiminny\\Component\\Twilio\\Exception\\OutOfBoundsException;\nuse Jiminny\\Component\\Twilio\\Exception\\UnexpectedValueException;\nuse Jiminny\\Component\\Twilio\\Resolver;\nuse Jiminny\\Component\\Twilio\\VO;\nuse Jiminny\\Component\\UrlGenerator\\Webhook;\nuse Jiminny\\Events;\nuse Jiminny\\Exceptions\\LogicException;\nuse Jiminny\\Models;\nuse Jiminny\\Models\\Participant\\Connection;\nuse Jiminny\\Services\\ActivityService;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Crm\\CachedCrmServiceDecorator;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Services\\Telephony\\TwilioClientBuilder;\nuse Psr\\Log\\LoggerInterface;\nuse Twilio\\Exceptions\\ConfigurationException;\nuse Twilio\\Exceptions\\RestException;\nuse Twilio\\Exceptions\\TwilioException;\nuse Twilio\\TwiML;\n\nfinal readonly class SoftPhoneManager implements ConferenceManager\n{\n public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;\n public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';\n\n public function __construct(\n private ProviderRegistry $crmProviderRegistry,\n private Webhook $urlGenerator,\n private ActivityService $activityService,\n private LoggerInterface $logger,\n private TwilioClientBuilder $twilioClientBuilder,\n ) {\n }\n\n public function create(VO\\ConferenceManager\\ActivityCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n 'instanceof' => get_class($data),\n ]);\n\n $activity = Observer::skipEvents(function () use ($data) {\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundCreate) {\n $activity = $this->createInboundActivity($data);\n } elseif ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundCreate) {\n $activity = $this->createOutboundActivity($data);\n } else {\n throw new LogicException(sprintf(\n 'Unknown softphone implementation: %s',\n get_class($data)\n ));\n }\n\n return $activity;\n });\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'instanceof' => get_class($data),\n 'activity' => $activity->id_string,\n ]);\n\n return $activity;\n }\n\n private function createOutboundActivity(VO\\ConferenceManager\\SoftPhoneOutboundCreate $data): Models\\Activity\n {\n $startTime = microtime(true);\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n $now = now();\n $user = $data->getUser();\n $team = $user->getTeam();\n $device = $data->getDevice();\n $phoneNumber = $data->getPhoneNumber();\n $route = $data->getPhoneRoute();\n\n if (! $this->activityService->teamCanDial($team, $phoneNumber)) {\n $region = getRegionByNumber($phoneNumber, $user->getCountryCode());\n\n throw new LogicException(sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? 'this number'\n ));\n }\n\n $lead = null;\n $account = null;\n $contact = null;\n $stage = null;\n $opportunity = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $user,\n 'providerSlug' => $team->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n } catch (\\Throwable $throwable) {\n $this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [\n 'exception' => $throwable,\n 'user_id' => $user->getId(),\n 'team_id' => $team->getId(),\n ]);\n $crmService = $this->crmProviderRegistry->get($team->crm->provider);\n if (! $user->isCrmRequired()) {\n $crmService->setUser($user->team->owner);\n } else {\n $crmService->setUser($user);\n }\n }\n\n $prospectId = $data->getProspectId();\n\n try {\n if ($prospectId !== null) {\n $crmStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [\n 'prospect_id' => $prospectId,\n 'object_type' => $data->getObjectType(),\n ]);\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(\n $prospectId,\n $data->getObjectType()\n );\n $this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),\n 'found_lead' => $lead !== null,\n 'found_account' => $account !== null,\n 'found_opportunity' => $opportunity !== null,\n 'found_contact' => $contact !== null,\n ]);\n\n // Try to attach a contact if we have an opportunity.\n if ($opportunity !== null && $contact === null) {\n $matchStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');\n [, , , $contact, ] = $crmService->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n $this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),\n 'found_contact' => $contact !== null,\n ]);\n }\n } else {\n $decorator = resolve(CachedCrmServiceDecorator::class);\n $decorator->setCrmService($crmService);\n\n [$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(\n $data->getPhoneNumber(),\n $data->getRawPhoneNumber(),\n $user->getId()\n );\n }\n } catch (\\Throwable $throwable) {\n $this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.\n }\n\n if ($data->hasProspectStage()) {\n /** @var Models\\Stage $stage */\n $stage = $team->crm->stages()->uuid($data->getProspectStage());\n }\n\n $dbStart = microtime(true);\n $this->logger->info('[SoftPhoneManager] Starting activity creation in DB');\n $activity = $user->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'device_id' => $device->id ?? null,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'value' => $opportunity ? $opportunity->value : null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_PENDING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),\n 'scheduled_start_time' => $now,\n ]);\n $this->logger->info('[SoftPhoneManager] Activity created in DB', [\n 'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),\n 'activity_id' => $activity->id ?? null,\n ]);\n\n if (! $activity instanceof Models\\Activity) {\n throw new LogicException('Activity model expected');\n }\n\n $regionId = null;\n if ($user->hasRegionId()) {\n $regionId = $user->getRegionId();\n } elseif ($data->hasRegion()) {\n $regionId = $data->getRegion()->getId();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $regionId,\n 'is_muted' => false,\n ]);\n\n $phoneRouteResolver = new Resolver\\PhoneRouteResolver($user, $route);\n $phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();\n $connectionType = $phoneRouteResolver->getConnectionType();\n $callerId = $this->determineUserCallerId($user);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'user_id' => $user->id,\n 'email' => $user->email,\n 'name' => $user->name,\n 'phone_number' => $phoneRouteResolver->mustUsePSTN()\n ? $phoneNumberOrigin\n : $callerId,\n ]);\n\n $caller->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',\n 'phone' => $callerId,\n 'type' => $connectionType,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $phoneNumber,\n 'name' => $activity->prospect_name,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'phone' => $phoneNumber,\n 'direction' => 'outbound-api',\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $activity->refresh();\n\n $this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [\n 'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),\n 'activity_id' => $activity->id_string,\n ]);\n $this->logger->debug(__METHOD__ . ': done', [\n 'data' => $data,\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n return $activity;\n }\n\n private function createInboundActivity(VO\\ConferenceManager\\SoftPhoneInboundCreate $data): Models\\Activity\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $roomOwner = $data->getUser();\n $team = $roomOwner->getTeam();\n $phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();\n $callerId = $data->getCallerId();\n $callerCallSid = $data->getCallSid();\n\n $now = now();\n\n if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {\n $region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());\n\n throw new LogicException(\n sprintf(\n 'Sorry, dialing %s is not available with your subscription.'\n . ' Please contact your manager to enable additional destinations.',\n $region ?? ' this number',\n ),\n );\n }\n\n $lead = $account = $opportunity = $contact = $stage = null;\n\n try {\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $roomOwner->getTeam(),\n 'integrationAdmin' => $roomOwner,\n 'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),\n ]);\n $crmService = $crmResolver->prepareCrmService();\n\n [$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(\n $callerId,\n null,\n $roomOwner->getId()\n );\n } catch (\\Throwable $throwable) {\n $this->logger->error(__METHOD__ . ': crm exception', [\n 'exception' => $throwable,\n ]);\n // maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening\n }\n\n /** @var Models\\Activity $activity */\n $activity = $roomOwner->activities()->create([\n 'type' => Models\\Activity::TYPE_SOFTPHONE_INBOUND,\n 'provider' => Models\\Activity::PROVIDER_TWILIO,\n 'crm_configuration_id' => $team->crm_id,\n 'lead_id' => $lead->id ?? null,\n 'account_id' => $account->id ?? null,\n 'contact_id' => $contact->id ?? null,\n 'opportunity_id' => $opportunity->id ?? null,\n 'stage_id' => $stage->id ?? null,\n 'status' => Models\\Activity::STATUS_RINGING,\n 'recording_state' => Models\\Activity::RECORDING_OFF,\n 'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),\n 'scheduled_start_time' => $now,\n ]);\n\n /** @var Models\\Session $session */\n $session = $activity->sessions()->create([\n 'type' => Models\\Session::TYPE_DIALER,\n 'region_id' => $roomOwner->getRegionId(),\n 'is_muted' => false,\n ]);\n\n /** @var Models\\Participant $caller */\n $caller = $activity->participants()->create([\n 'lead_id' => $activity->lead_id ?? null,\n 'contact_id' => $activity->contact_id ?? null,\n 'phone_number' => $callerId,\n 'name' => $activity->prospect_name,\n 'enter_time' => $now,\n ]);\n\n $caller->connections()->create([\n 'telephony_provider_id' => $callerCallSid,\n 'session_id' => $session->getId(),\n 'phone' => $callerId,\n 'direction' => 'inbound',\n 'type' => Connection::TYPE_PSTN,\n 'opened_at' => $now,\n ]);\n\n /** @var Models\\Participant $callee */\n $callee = $activity->participants()->create([\n 'user_id' => $roomOwner->id,\n 'email' => $roomOwner->email,\n 'name' => $roomOwner->name,\n 'phone_number' => $phoneNumber,\n ]);\n\n $callee->connections()->create([\n 'session_id' => $session->getId(),\n 'direction' => 'outbound-api',\n 'phone' => $phoneNumber,\n 'type' => Connection::TYPE_PSTN,\n ]);\n\n $activity->update([\n 'from_participant_id' => $caller->id,\n 'to_participant_id' => $callee->id,\n ]);\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'callSid' => $data->getCallSid(),\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n 'caller' => $caller->id_string,\n 'callee' => $callee->id_string,\n ]);\n\n $activity->refresh();\n\n return $activity;\n }\n\n private function getVoiceRecordingReasonCode(Models\\Team $team, string $phoneNumber): int\n {\n $this->logger->debug(__METHOD__, [\n 'team' => $team->id_string,\n 'phone' => $phoneNumber,\n ]);\n\n if (\n ! $team->hasComplianceModeRecordingPrompt()\n && ! $team->hasComplianceModeRecordingRestrictToOneSide()\n && ! $team->hasComplianceModeRecordingRestrictRecording()\n ) {\n return 0;\n }\n\n /** @var Models\\VoiceConsentPrefix|null $voiceConsentPolicy */\n $voiceConsentPolicy = Models\\VoiceConsentPrefix::query()\n ->whereRaw(\"? LIKE CONCAT(destination_prefix, '%')\", [$phoneNumber])\n ->where(static function (Eloquent\\Builder $builder) use ($team): void {\n $builder\n ->whereNull('team_id')\n ->orWhere('team_id', $team->getId());\n })\n ->orderByDesc('team_id')\n ->first();\n\n if (! $voiceConsentPolicy instanceof Models\\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {\n return 0;\n }\n\n if ($team->hasComplianceModeRecordingPrompt()) {\n // This number requires consent from the other party before recording the call.\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;\n }\n\n if ($team->hasComplianceModeRecordingRestrictToOneSide()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;\n }\n\n if ($team->hasComplianceModeRecordingRestrictRecording()) {\n return Models\\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;\n }\n\n return 0;\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOrganiser(Models\\Activity $activity): void\n {\n $this->logger->debug(__METHOD__, [\n 'activity' => $activity->id_string,\n ]);\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $canBeDialed =\n $activity->isTypeSoftPhone()\n && ! $activity->hasStarted()\n && $activity->isPending()\n && $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->exists();\n\n if (! $canBeDialed) {\n throw new OutOfBoundsException('Cannot make an outbound call to this activity.');\n }\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->where('type', '=', Connection::TYPE_PSTN)\n ->whereNull('opened_at')\n ->firstOrFail();\n\n /** @var Models\\Session $session */\n $session = $organiserConnection->getSession();\n\n $twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ]);\n\n // What number will the organizer see as caller id\n $callerId = $roomOwner->getSoftPhoneNumber();\n\n if ($callerId === null) {\n $callee = $activity->participants()->whereNull('user_id')->first();\n // This is a bridge call. We make it look like the callee is calling the organizer\n $callerId = $callee->phone_number;\n }\n\n // What number will the prospect see as caller id\n $calleeId = $organiser->getPhoneNumber();\n\n $this->logger->debug(__METHOD__ . ': twilio call', [\n 'calleeId' => $calleeId,\n 'callerId' => $callerId,\n ]);\n\n $twilioParticipant = $twilio->createParticipantInstance(\n $session->id_string,\n $callerId,\n $calleeId,\n $twilioConferenceSettings\n );\n\n $organiserConnection\n ->setTelephonyProviderId($twilioParticipant->callSid)\n ->save();\n\n $session\n ->setTelephonyProviderId($twilioParticipant->conferenceSid)\n ->save();\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function dialCoach(Models\\Activity $activity, Models\\User $user): void\n {\n throw new \\LogicException('Not implemented yet');\n }\n\n /**\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function terminate(VO\\ConferenceManager\\ActivityTerminate $data): void\n {\n if (! $data instanceof VO\\ConferenceManager\\SoftphoneTerminate) {\n throw new UnexpectedValueException(sprintf(\n 'Expecting an instance of \"%s\", but \"%s\" given.',\n VO\\ConferenceManager\\SoftphoneTerminate::class,\n \\get_class($data)\n ));\n }\n\n $this->logger->debug(__METHOD__, [\n 'activity' => $data->getActivity()->id_string,\n 'user' => $data->getUser()->id_string,\n ]);\n\n $activity = $data->getActivity();\n $roomOwner = $activity->getUser();\n\n $isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();\n\n if ($isCancelling) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n }\n\n /** @var Models\\Session $session */\n $session = $activity->sessions->first();\n\n /** @var Models\\Participant $organiser */\n $organiser = $activity->organizer;\n\n /** @var Connection $organiserConnection */\n $organiserConnection = $organiser->connections()\n ->whereIn('type', [\n Connection::TYPE_WEBRTC,\n Connection::TYPE_PSTN,\n ])\n ->firstOrFail();\n\n if (! $organiserConnection->hasTelephonyProviderId()) {\n throw new LogicException(\n 'Unable to terminate call as we do not yet know the organiser telephony provider id',\n );\n }\n\n $twilioClient = $this->twilioClientBuilder->build($activity->getTeam());\n\n $twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());\n\n $session->hasTelephonyProviderId()\n && $twilioClient->terminateConference($session->getTelephonyProviderId());\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'session' => $session->id_string,\n ]);\n }\n\n public function answer(VO\\ConferenceManager\\ActivityAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'data' => $data,\n ]);\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneInboundAnswer) {\n return $this->answerInboundCall($data);\n }\n\n if ($data instanceof VO\\ConferenceManager\\SoftPhoneOutboundAnswer) {\n return $this->answerOutboundCall($data);\n }\n\n throw new LogicException('Unable to answer - not supported activity answer instance');\n }\n\n private function answerInboundCall(VO\\ConferenceManager\\SoftPhoneInboundAnswer $data): TwiML\\VoiceResponse\n {\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n ]);\n\n $dateTime = now();\n\n $activity = $this->create(\n new VO\\ConferenceManager\\SoftPhoneInboundCreate(\n $data->getCallerId(),\n $data->getCallSid(),\n $data->getUser()\n )\n );\n\n /** @var Models\\Participant $activityGuest */\n $activityGuest = $activity->from;\n\n $activityGuest\n ->setEnterTime($dateTime)\n ->save();\n\n /** @var Connection $connection */\n $connection = $activityGuest->connections()\n ->where('type', Connection::TYPE_PSTN)\n ->firstOrFail();\n\n $connection\n ->setTelephonyProviderId($data->getCallSid())\n ->setOpenedAt($dateTime)\n ->save();\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n return $this->joinTwilioConference($connection, [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]);\n }\n\n private function answerOutboundCall(VO\\ConferenceManager\\SoftPhoneOutboundAnswer $data): TwiML\\VoiceResponse\n {\n $connection = $data->getConnection();\n $participant = $connection->getParticipant();\n $activity = $data->getActivity();\n $organiser = $activity->organizer;\n $dateTime = now();\n\n $this->logger->debug(__METHOD__, [\n 'callSid' => $data->getCallSid(),\n 'connection' => $connection->id_string,\n 'participant' => $participant->id_string,\n 'activity' => $activity->id_string,\n ]);\n\n if (! $participant->hasEnterTime()) {\n $participant->update([\n 'enter_time' => $dateTime,\n ]);\n }\n\n $connection->update([\n 'is_muted' => $participant->isCoach(),\n 'opened_at' => $dateTime,\n ]);\n\n event(new Events\\Activities\\Connections\\Opened($connection));\n\n if ($participant->isCoach()) {\n /** @var Connection $roomOwnerConnection */\n $roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'coach' => $roomOwnerConnection->getTelephonyProviderId(),\n ]\n );\n }\n\n return $this->joinTwilioConference(\n $connection,\n [\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n 'muted' => false,\n ]\n );\n }\n\n private function determineUserCallerId(Models\\User $user): string\n {\n if ($user->hasCallerId()) {\n return $user->getCallerId();\n }\n\n // TODO: We should require a caller id to be verified before using the dialer\n return $user->getSoftPhoneNumber() ?? $user->phone;\n }\n\n private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool\n {\n $startsWith = '[HTTP 404] Unable to create record: The requested resource';\n $endsWith = 'was not found';\n\n return Str::startsWith($exceptionMessage, $startsWith)\n && Str::endsWith($exceptionMessage, $endsWith);\n }\n\n /**\n * @throws RestException\n * @throws ConfigurationException\n * @throws TwilioException\n */\n public function dialOtherParty(Connection $callerConnection): self\n {\n $session = $callerConnection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n $twilio = $this->twilioClientBuilder->build($activity->getTeam());\n\n $this->logger->debug(__METHOD__, [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {\n throw new UnexpectedValueException('Unexpected activity type');\n }\n\n $callee = $activity->to;\n $calleeId = $callee->getPhoneNumber();\n\n /** @var Connection $calleeConnection */\n $calleeConnection = $callee->connections()\n ->whereIn('type', [\n Connection::TYPE_PSTN,\n Connection::TYPE_WEBRTC,\n ])\n ->firstOrFail();\n\n $caller = $activity->from;\n\n // we need to take the number from the connection\n // as the one we store on participant level might be the one we call to the rep via PSTN mode\n $callerId = $callerConnection->getPhoneNumber();\n\n if (! $caller->is($callerConnection->getParticipant())) {\n throw new LogicException(sprintf(\n 'Unexpected caller participant. Expected: \"%s\", Actual: \"%s\"',\n $caller->id_string,\n $callerConnection->getParticipant()->id_string\n ));\n }\n\n $twilioConferenceSettings = [\n 'muted' => false,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => true,\n ];\n\n $hasInboundRecording = $activity->isTypeSoftphoneInbound()\n && $roomOwner->checkSoftphoneInboundRecordPreference();\n\n $hasOutboundRecording = $activity->isTypeSoftPhone()\n && $roomOwner->checkSoftphoneOutboundRecordPreference();\n\n $isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;\n\n $isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()\n || $activity->hasRecordingReasonComplianceRestricted();\n\n if ($isRecordingEnabled && ! $isRecordingRestricted) {\n $twilioConferenceSettings = array_merge($twilioConferenceSettings, [\n 'record' => true,\n 'recordingChannels' => 'dual',\n 'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),\n 'recordingStatusCallbackMethod' => 'POST',\n 'recordingStatusCallbackEvent' => 'in-progress completed absent',\n ]);\n\n if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {\n $twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()\n ? 'outbound'\n : 'inbound';\n }\n }\n\n try {\n if ($activity->hasRecordingReasonCompliancePrompted()) {\n $this->logger->info('[Softphone] Request compliance prompt via outbound call', [\n 'activity_uuid' => $activity->getUuid(),\n 'session' => $session->id_string,\n 'conference_sid' => $session->getTelephonyProviderId(),\n ]);\n\n $callInstance = $twilio->createCall(\n $callerId,\n $calleeId,\n $this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [\n 'session' => $session->id_string,\n 'connection' => $calleeConnection->id_string,\n ]),\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $callInstance->sid,\n 'parent_connection_id' => $callerConnection->getId(),\n ]);\n } else {\n $twilioParticipantConnection = $twilio->createParticipantInstance(\n $session->getTelephonyProviderId(),\n $callerId,\n $calleeId,\n $this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)\n );\n\n $calleeConnection->update([\n 'telephony_provider_id' => $twilioParticipantConnection->callSid,\n ]);\n }\n } catch (RestException $restException) {\n if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {\n throw $restException;\n }\n\n if (! $activity->isInFiniteState()) {\n $activity\n ->setStatus(Models\\Activity::STATUS_CANCELLED)\n ->save();\n\n $message = __METHOD__ . ': activity.status -> cancelled';\n } else {\n $message = sprintf(\n __METHOD__ . ': not setting to cancelled, activity.status is %s',\n $activity->getStatus()\n );\n }\n\n $this->logger->info($message, [\n 'activity' => $activity->id_string,\n 'exception' => $restException,\n ]);\n }\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n 'connection' => $callerConnection->id_string,\n ]);\n\n return $this;\n }\n\n private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array\n {\n $session = $connection->getSession();\n\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => true,\n 'endConferenceOnExit' => false,\n 'record' => false,\n 'waitUrl' => '',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]),\n 'statusCallbackEvent' => [\n 'initiated',\n 'ringing',\n 'answered',\n 'completed',\n ],\n 'statusCallbackMethod' => 'POST',\n 'conferenceStatusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n ]),\n 'conferenceStatusCallbackMethod' => 'POST',\n 'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function applyDefaultTwilioConferenceSettings(Models\\Session $session, array $override = []): array\n {\n $default = [\n 'beep' => false,\n 'muted' => true,\n 'startConferenceOnEnter' => false,\n 'endConferenceOnExit' => false,\n 'waitUrl' => '',\n 'statusCallbackEvent' => implode(' ', [\n 'start',\n 'end',\n 'join',\n 'leave',\n 'mute',\n 'hold',\n// 'speaker',\n ]),\n 'statusCallbackMethod' => 'POST',\n 'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [\n 'session' => $session->getIdString(),\n ]),\n ];\n\n if ($session->hasRegion()) {\n $default['region'] = $session->getRegion()->getName();\n }\n\n return array_merge($default, $override);\n }\n\n private function joinTwilioConference(Connection $connection, array $options = []): TwiML\\VoiceResponse\n {\n $session = $connection->getSession();\n $activity = $session->getActivity();\n $roomOwner = $activity->getUser();\n\n $this->logger->debug(__METHOD__, [\n 'connection' => $connection->id_string,\n 'session' => $session->id_string,\n 'activity' => $activity->id_string,\n 'user' => $roomOwner->id_string,\n ]);\n\n $voiceResponse = new TwiML\\VoiceResponse();\n\n $voiceResponse->dial()\n ->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [\n 'connection' => $connection->id_string,\n ]))\n ->conference(\n $session->getIdString(),\n $this->applyDefaultTwilioConferenceSettings($session, $options)\n );\n\n $this->logger->debug(__METHOD__ . ': done', [\n 'session' => $session->id_string,\n 'result' => $voiceResponse,\n ]);\n\n return $voiceResponse;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.42785904,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.43650267,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.4474734,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.45611703,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.46476063,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.47573137,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"bounds":{"left":0.4867021,"top":0.09896249,"width":0.024268618,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"bounds":{"left":0.51329786,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"bounds":{"left":0.5242686,"top":0.09896249,"width":0.029587766,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny_jupiter","depth":4,"bounds":{"left":0.69348407,"top":0.09896249,"width":0.04089096,"height":0.01915403},"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":"19","depth":4,"bounds":{"left":0.6871675,"top":0.123703115,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"19","depth":4,"bounds":{"left":0.6988032,"top":0.123703115,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"17","depth":4,"bounds":{"left":0.71043885,"top":0.123703115,"width":0.00930851,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.72140956,"top":0.12210695,"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.7287234,"top":0.12210695,"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":"SELECT * FROM teams WHERE id = 1;\n\nselect * from crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 283;\nSELECT * FROM crm_fields WHERE id = 2234;\nSELECT * FROM crm_field_values WHERE crm_field_id = 2234;\n\nselect * from crm_profiles where user_id = 143;\n\nselect * from record_types where crm_configuration_id = 39; # 0121K000001MHElQAO,0121K000001MHEqQAO\nselect * from business_processes where crm_configuration_id = 39;\n# 01941000000H669AAC, 01941000000H66JAAS\n\nselect * from record_type_field_values\n where record_type_id IN (24);\n\nselect * from crm_field_values where id IN (2730);\n\nselect * from crm_configurations where id = 39;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce'; #1035\n\n\nselect * from users where team_id = 1; # 222 group 3\nSELECT * FROM activities WHERE user_id = 222 order by id desc;\nselect * from sidekick_settings where team_id = 1;\nselect * from teams where id = 1;\nselect * from team_features where team_id = 1;\n\nselect * from activities where crm_configuration_id = 2\nand provider = 'ms-teams' and id = 608765;\n\nSELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id = '59523413338';\n\nselect * from sidekick_settings where team_id = 2;\n\nSELECT * FROM activities WHERE id = 608660;\nselect * from activity_summary_logs where activity_id = 608660;\nselect * from ai_prompts where transcription_id = 11214;\n\n# ********************************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('ed78a437-2804-450e-ab2f-56ab1c641346') = uuid;\n# id: 608818, crm: 59628809737\nSELECT * FROM activities WHERE uuid_to_bin('36b06e55-afdd-4782-8dee-c624cd0af191') = uuid;\n# id: 608821, crm: 59632069252\nSELECT ce.start_time, ce.end_time, a.id, a.uuid, crm_provider_id, calendar_event_id, title,\nplaybook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id,\nscheduled_start_time, scheduled_end_time, actual_start_time, actual_end_time, a.created_at\nFROM activities a\njoin calendar_events ce on a.calendar_event_id = ce.id\nWHERE a.id IN (608818, 608821);\n\nselect * from users where team_id = 1;\nselect * from team_settings where team_id = 1;\nselect * from crm_profiles where crm_configuration_id = 39 order by user_id;\n\nselect * from team_features where team_id = 1;\n\nselect * from users where team_id = 2;\n\nSELECT * FROM activities WHERE uuid_to_bin('ec7647e9-5225-458b-b475-f31aa2769204') = uuid; # 612639\n# Preslava N. Ivanova, grou id 3\n\nSELECT * FROM opportunities WHERE uuid_to_bin('a2928fe5-aec5-46cb-85d9-7654c89e46a6') = uuid;\n\nselect * from activities where opportunity_id = 344 and actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00';\n\nselect\n a.id,\n a.type,\n a.scheduled_start_time,\n a.actual_start_time,\n a.created_at,\n a.opportunity_id,\n a.status\nFROM activities a\nWHERE opportunity_id = 344\nand status IN ('completed', 'received', 'delivered')\nand (\n (a.actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')\nOR (a.created_at between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')\nOR (a.scheduled_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00'))\n;\n\nSELECT * FROM users WHERE id = 222;\n\nSELECT * FROM crm_profiles WHERE user_id = 222;\nselect * from crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 281;\n\nselect * from group_deal_risk_types;\n\nselect * from opportunities where team_id = 1;\n\nSELECT * FROM opportunities WHERE id = 315;\nSELECT * FROM crm_field_data WHERE object_id = 315;\nselect * from crm_field_data where object_id = 260;\n\nselect * from generic_ai_prompts where subject_id = 315;\n\nselect * from teams; # 36, 21, 121, james.graham@bullhorn.jiminny.com\nSELECT * FROM social_accounts WHERE sociable_id = 121 and provider = 'bullhorn';\n\n# ************************************************************************************\nselect * from teams where id = 1;\nselect * from crm_configurations where id = 39;\nselect * from users where team_id = 1;\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 1;\n# 1 - 00541000004281rAAA\n# 204 - 0052g000003freeAAA\n# 429 - 0052g000003qGOiAAM\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce';\n\nselect * from activities where type = 'softphone'\nand created_at > '2024-12-11 15:24:36' order by id desc;\n\nselect * from activity_providers where team_id = 1;\nselect * from activity_provider_users where activity_provider_id = 328;\n\nselect * from opportunities where crm_configuration_id = 39\nAND account_id = 178 AND is_closed = false\norder by created_at DESC;\n\nselect * from contacts where id = 3952;\nselect * from accounts where id = 178;\n\n# ************************************************************************************\nselect * from teams where id = 36;\nselect * from crm_configurations where id = 21;\nselect * from users where team_id = 36;\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 36;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 36\nand sa.provider = 'bullhorn';\n\nselect * from social_accounts where id = 348;\nUPDATE social_accounts SET\nprovider_user_token = '21442_6802599_91:41179a58-21e7-4d7c-ad58-56bb666b2f65',\nprovider_refresh_token = '21442_6802599_91:01c6b335-3f2a-42e4-85ff-8a08fa65fceb',\nexpires = 1733998131,\nstate = 'connected'\nWHERE id = 348;\n\n# ************************************************************************************\nselect * from teams where id = 31;\nselect * from crm_configurations where id = 18;\n\nselect * from users where team_id = 31; # 257\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 31;\n\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 31\nand sa.provider = 'close';\n\nselect * from contacts where crm_configuration_id = 18;\n\n# ********************** NEPTUNE **************************************************************\nselect * from teams;\nselect * from users where id IN (1030, 1035, 1052);\nselect * from crm_configurations;\n\nselect * from users where team_id = 65; # 257\nselect * from team_settings where team_id = 65; # 257\nselect * from invitations where team_id = 65; # 257\nselect * from users where email = 'integration-account@jiminny.com'; # 257\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 65;\n\nselect * from crm_configurations where id = 53;\nselect * from accounts where crm_configuration_id = 53 order by id desc;\nselect * from leads where crm_configuration_id = 53 order by id desc;\nselect * from contacts where crm_configuration_id = 53 order by id desc;\nselect * from opportunities where crm_configuration_id = 53 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 53 order by id desc;\nselect * from crm_fields where crm_configuration_id = 53 order by id desc;\nselect * from crm_field_values where crm_field_id = 3341 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 53 order by id desc;\nselect * from stages where crm_configuration_id = 53 order by id desc;\n\n\nselect * from crm_profiles where crm_configuration_id = 13;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 65\nand sa.provider = 'integration-app';\n\nselect * from contacts where crm_configuration_id = 13;\n\nselect * from social_accounts where sociable_id = 283;\n\nSELECT * FROM opportunities WHERE crm_provider_id = '006O400000E9bzeIAB';\n\nselect * from activity_providers where team_id = 65;\nSELECT * FROM activities WHERE crm_configuration_id IN (51, 52, 53);\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 65\n;\n\n# ***************************** STAGING ********************************************\nSELECT * FROM teams;\nSELECT * FROM teams WHERE id = 88;\nSELECT * FROM teams WHERE id = 89;\nselect * from team_settings where team_id = 89;\nSELECT * FROM users WHERE team_id = 89;\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 89;\n\nselect * from users;\nSELECT * FROM social_accounts WHERE sociable_id = 1761;\nSELECT * FROM crm_configurations WHERE id = 70;\nselect * from accounts where crm_configuration_id = 70 order by id desc;\nselect * from leads where crm_configuration_id = 70 order by id desc;\nselect * from contacts where crm_configuration_id = 70 order by id desc;\nselect * from opportunities where crm_configuration_id = 70 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 70 order by id desc;\nselect * from crm_fields where crm_configuration_id = 70 order by id desc;\nselect * from crm_field_values where crm_field_id = 3536 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 70 order by id desc;\nselect * from stages where crm_configuration_id = 70 order by id desc;\nselect * from business_processes where crm_configuration_id = 70 order by id desc;\nselect * from business_process_stages where business_process_id = 34;\n\nselect * from contacts where id = 10468;\n\nselect * from crm_layouts where crm_configuration_id = 70;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 388;\nSELECT * FROM crm_fields WHERE id IN (3533,3534,3535);\n\nselect * from activities where crm_configuration_id = 70\nand (account_id IS NOT NULL or lead_id IS NOT NULL or contact_id IS NOT NULL or opportunity_id IS NOT NULL) order by id desc;\n\nSELECT * FROM activities WHERE uuid_to_bin('2e10b60f-8a61-41c5-a3d4-28835353dc65') = uuid;\nSELECT * FROM activities where crm_configuration_id = 69 ;\n\nSELECT * FROM users WHERE email LIKE '%jiminny_web_sa2@jiminny.com%';\nSELECT * FROM activities WHERE uuid_to_bin('5a150c93-40fc-42ec-b3bd-c1d328e09f6e') = uuid;\nSELECT * FROM opportunities WHERE id = 385;\n\nselect * from participants p\njoin activities a on p.activity_id = a.id\nwhere a.crm_configuration_id = 70\nand (p.lead_id IS NOT NULL or p.contact_id IS NOT NULL);\nSELECT * FROM participants WHERE id = 1013638;\n\nselect * from teams where id = 90;\nselect * from users where team_id = 90;\nselect * from social_accounts where social_accounts.sociable_id IN (1960,1760);\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 71;\nselect * from invitations where team_id = 90;\n\nselect * from crm_configurations where id = 71;\nselect * from accounts where crm_configuration_id = 71 order by id desc;\nselect * from leads where crm_configuration_id = 71 order by id desc;\nselect * from contacts where crm_configuration_id = 71 order by id desc;\nselect * from opportunities where crm_configuration_id = 71 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 71 order by id desc;\nselect * from crm_fields where crm_configuration_id = 71 order by id desc;\nselect * from crm_field_values where crm_field_id = 3341 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 71 order by id desc;\nselect * from stages where crm_configuration_id = 71 order by id desc;\n\nselect * from users order by secondary_email desc;\nselect u.id, u.email, u.status, sa.id, sa.provider_user_id from social_accounts sa\n join users u on sa.sociable_id = u.id\nwhere sa.provider = 'google' and u.email LIKE 'aneliya%';\n\nselect * from failed_jobs order by id desc;\n\nselect * from users where email = 'ben.allwright@learningpeople.co.uk' or secondary_email = 'ben.allwright@learningpeople.co.uk';\n\nselect * from teams;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 39;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type = 'task';\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce';\n\n# ************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('c38b3895-fd0f-4b1f-9fb2-c170dba137c6') = uuid;\nSELECT * FROM crm_configurations WHERE id = 70;\n\nselect * from teams where id = 1;\nselect * from groups where team_id = 1;\nselect * from users where team_id = 1;\n\nselect o.id, o.name,o.close_date, u.id, u.name, u.group_id, r.id, r.display_name, g.name, g.scope from opportunities o\njoin users u on o.user_id = u.id\njoin groups g on u.group_id = g.id\njoin role_user ru on u.id = ru.user_id\njoin roles r on ru.role_id = r.id\nwhere o.crm_configuration_id = 39 and close_date > '2024-01-01 00:00:00';\n\nselect * from role_user where user_id = 143;\nselect * from roles;\n\nselect * from role_user;\nselect * from groups where id = 9;\nselect * from scope_groups where group_id = 9;\n\n# ************************************************************************************\nselect * from teams where id = 36;\nselect * from crm_configurations;\nSELECT * FROM social_accounts WHERE sociable_id = 121;\n\nhttps://crmsandbox.zoho.com/crm/jiminnyw4/tab/Leads/4776201000005049105\nhttps://crmsandbox.zoho.com/crm/\n\nhttps://crm.zoho.com/crm/org3469620/tab/Leads/230045000229559080\n https://crm.zoho.com/crm/\n org3469620\n\nSELECT * FROM activities WHERE uuid_to_bin('03382d20-c8bc-48e7-a3d4-90b52fa5ceab') = uuid;\n\nselect * from users where email LIKE \"%mobile_automation_%\";\nselect * from social_accounts where sociable_id IN (2228);\nselect * from crm_profiles where user_id IN (2222,2223,2226,2227);\n\nselect * from teams order by id desc;\nSELECT * FROM users WHERE id = 2229;\nSELECT * FROM crm_profiles WHERE user_id = 2229;\nselect * from opportunities where crm_configuration_id = 88;\nselect * from crm_fields where crm_configuration_id = 88;\nselect * from crm_profiles where crm_configuration_id = 88;\n\nSELECT * FROM teams WHERE id = 1;\n\nSELECT * FROM users WHERE id = 143;\nSELECT * FROM users WHERE uuid_to_bin('fde193d3-06a2-4e1a-8895-62b94039215d') = uuid;\nSELECT * FROM teams WHERE uuid_to_bin('73385071-a756-42ae-9c73-8b53f2309467') = uuid;\n\nhttps://app.staging.jiminny.com/ondemand?\n min_duration=1\n &\n only_recorded=1\n &\n user_id%5B%5D=641f1acb-16b8-42d1-8726-df52979dad0e\n &\n sequence_number=2\n\n select * from users where team_id = 1 and email like '%stoyan%'\n\nselect * from coaching_feedbacks;\n\nselect * from teams;\nSELECT * FROM users WHERE team_id = 36;\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 19\nand sa.provider = 'pipedrive';\n\nselect * from users where id = 143;\n\nSELECT * FROM users WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\nSELECT * FROM teams WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\nSELECT * FROM activity_shares WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\n\nselect * from users where team_id = 2;\nselect * from activities where crm_configuration_id = 39\nand activities.scheduled_start_time BETWEEN '2025-04-09 00:00:00' AND '2025-04-09 23:59:59'\nAND user_id = 143\norder by id desc;\n\n# ************************************************************************************\nselect * from teams where id = 142; # 2312, 126\nselect * from team_settings;\nselect * from users where team_id = 142; # 21642\nSELECT * FROM social_accounts WHERE sociable_id = 21642;\nSELECT * FROM crm_profiles cp join users u ON u.id = cp.user_id WHERE team_id = 142;\nselect * from crm_profiles where id IN (93);\nselect * from invitations;\nselect * from team_features where team_id = 1;\n\nSELECT * FROM crm_configurations WHERE id = 126;\nselect * from accounts where crm_configuration_id = 126 order by id desc;\nselect * from leads where crm_configuration_id = 126 order by id desc;\nselect * from contacts where crm_configuration_id = 126 order by id desc;\nselect * from opportunities where crm_configuration_id = 126 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 126 order by id desc;\nselect * from crm_fields where crm_configuration_id = 126 # 11060\n# and type IN ('picklist', 'status')\n# and object_type = 'task'\norder by id desc;\n# 5731,5732,5733\nselect DISTINCT crm_field_id from crm_field_values where crm_field_id IN (11151,12239,12215,12185,12175,12165,12144,12137,12127,12109,12107,12105,12103,12092,12037,12005,12003,11987,11969,11958,11951,11942,11931,11924,11921,11917,11915,11901,11893,11883,11872,11870,11868,11866,11839,11833,11821,11793,11780,11777,11769,11757,11737,11735,11656,11645,11638,11629,11618,11611,11602,11591,11584,11581,11558,11544,11543,11534,11532,11529,11527,11503,11497,11493,11488,11470,11468,11457,11455,11397,11387,11372,11363,11348,11323,11318,11309,11301,11300,11292,11290,11286,11284,11256,11252,11242,11237,11233,11219,11176,11160) order by id desc;\nselect * from crm_layouts where crm_configuration_id = 126 order by id desc;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id in (300,299,298);\nselect * from stages where crm_configuration_id = 126 order by id desc;\nselect * from business_processes where crm_configuration_id = 126 order by id desc;\nselect * from business_process_stages where business_process_id IN (76,75,74,73);\nselect * from playbooks where team_id = 142;\nselect * from playbook_layouts where playbook_id IN (108);\nSELECT * FROM playbook_categories WHERE playbook_id IN (108);\n\nselect * from teams where id = 130;\nSELECT * FROM social_accounts WHERE sociable_id = 2291;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 2\nand sa.provider = 'hubspot';\n\nSELECT * FROM activities\n WHERE crm_configuration_id = 110;\n\nselect * from teams;\nselect * from crm_configurations;\n\nSELECT * FROM activities WHERE id = 628773;\nSELECT * FROM crm_profiles WHERE user_id = 1460;\nSELECT * FROM social_accounts WHERE sociable_id = 2291;\n\nselect * from teams;\nselect ru.*, pr.*, p.* from users u join role_user ru on ru.user_id = u.id\njoin permission_role pr on pr.role_id = ru.role_id\n join permissions p on p.id = pr.permission_id\nwhere team_id = 495 and p.name IN ('dial');\n\nselect * from teams where id = 145;\nselect * from crm_configurations where id = 129;\nselect * from social_accounts where sociable_id = 2317;\nSELECT * FROM activities WHERE uuid_to_bin('8dbab184-a333-4268-ad57-fb41f8d53a9a') = uuid;\n\nselect * from teams where id = 1;\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 280;\nSELECT * FROM crm_layout_entities WHERE id = 5507;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type IN ('event');\n\nselect * from teams;\nselect * from activities where crm_configuration_id = 14;\n\nSELECT * FROM social_accounts where provider = 'copper';\n\nselect * from activities where id = 628467;\nselect * from participants where activity_id = 628467;\n\nSELECT * FROM contacts WHERE id = 3969;\nSELECT * FROM accounts WHERE id = 177;\n\nSELECT * FROM activities WHERE uuid_to_bin('4eb54c77-cfa3-2bd4-84a7-9ed46a21c988') = uuid;\n\n# ********************* BH\nselect * from teams where id = 36;\nSELECT * FROM crm_configurations WHERE id = 21;\nselect * from activities where crm_configuration_id = 21 and id = 607901;\nselect * from activities where crm_configuration_id = 21;\n\nselect * roles;\nselect * from permissions;\nselect * from permission_role where permission_id = 226;\n\nselect * from migrations order by id desc;\n\n# mercury\n# neptune\n# earth\n\nselect * from teams;\nselect * from teams where id = 19;\nselect * from teams where id = 27;\nselect * from users where team_id = 27;\nSELECT * FROM crm_configurations WHERE id = 42;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 19\nand sa.provider = 'pipedrive';\n\nselect * from activities where id = 631461;\nSELECT * FROM crm_field_values WHERE crm_field_id = 180;\n\nselect * from teams where id = 2;\nSELECT * FROM social_accounts WHERE sociable_id = 89;\n\nSELECT * FROM activities WHERE uuid_to_bin('ba0c029a-bc14-4e17-8603-64174acebcbb') = uuid; # 634273\nselect * from activity_summary_logs where activity_id = 634273;\n\nselect * from sidekick_settings where team_id = 2;\n\nselect * from teams; # 2, 2\nSELECT * FROM crm_configurations WHERE team_id = 2; # 2\nselect * from team_features where team_id = 2;\nselect * from features;\nSELECT * FROM opportunities WHERE crm_configuration_id = 2 and crm_provider_id = '51317301383';\nSELECT * FROM opportunities WHERE crm_configuration_id = 2 order by id desc;\n\nselect * from automated_reports order by id desc;\nselect * from automated_report_results order by id desc;\nselect * from users where team_id = 1 and id IN (7160, 3248);\nselect * from migrations order by id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1052 and sa.provider = 'hubspot';\n\nselect * from teams where id = 1;\nselect * from groups g JOIN playbooks p on g.playbook_id = p.id where g.team_id = 1;\nselect * from groups where id = 565;\nselect * from playbooks where team_id = 1;\nselect * from playbooks where id = 175;\nselect * from playbook_categories where playbook_id = 175;\nselect * from users where team_id = 1052;\nselect * from users where id = 7160;\nselect * from crm_profiles where user_id = 7160;\nselect * from features;\nselect\n *\n# id, uuid, type, provider, playbook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id, stage_id,\n# crm_configuration_id, crm_provider_id, transcription_id, status\nfrom activities where crm_configuration_id = 1 and type = 'conference'\n# and crm_provider_id IS NOT NULL\nand provider != 'uploader' and actual_start_time IS NOT NULL\nORDER by id desc;\nselect * from activities where id = 54747783; # 00UO400000pCzojMAC\n\nselect p.id, p.activity_type, pc.id, pc.name\nFROM playbooks p\njoin playbook_categories pc on p.id = pc.playbook_id\nwhere p.team_id = 1 and p.activity_type = 'event';\n\nSELECT * FROM crm_fields WHERE crm_configuration_id = 1 and object_type = 'event';\nSELECT * FROM crm_field_values WHERE crm_field_id = 4;\n\nselect * from crm_layouts cl join playbook_layouts pl on cl.id = pl.layout_id\nwhere crm_configuration_id = 1 and pl.playbook_id = 175;\n\nselect * from teams;\nSELECT r.* FROM automated_reports r\njoin teams t on r.team_id = t.id\nWHERE r.frequency = 'daily'\n and r.status = 1\nAND t.status = 'active'\nAND (r.expires_at >= now() OR r.expires_at IS NULL);\n\nselect * from automated_report_results where report_id IN (18, 33);\n\nselect * from users where team_id = 1 and id = 1047;\nSELECT * FROM social_accounts WHERE sociable_id = 1047;\n\nselect * from activity_searches where id = 10932;\nselect * from activity_search_filters where activity_search_id = 10932;\nselect * from automated_reports order by id desc;\nselect * from automated_report_results order by id desc;\nselect * from automated_reports where id IN (55);\nselect * from automated_report_results where id IN (81);\nselect * from users where id IN (10633, 13987, 11985);\nselect * from users where group_id IN (3710);\n\nSELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;\nSELECT * FROM automated_report_results WHERE uuid_to_bin('582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;\n\nselect * from teams;\nselect * from accounts where team_id = 1;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2;\nSELECT * FROM automated_report_results WHERE uuid_to_bin('82e74956-6144-4cd1-a3d3-af985c3070a4') = uuid;\n\nselect * from teams where id = 1029;\nselect * from crm_configurations where provider = 'pipedrive';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1029 and sa.provider = 'pipedrive';\n\n[\n {\n \"user_id\": \"23460 (owner)\",\n \"email\": \"integration-account@pipedrive.jiminny.com\",\n \"id\": 69,\n \"sociable_id\": 23460,\n \"provider_user_id\": \"19555731\",\n \"provider_user_token\": \"v1u:AQIBAHj-LzTNK2yuuuaLqifzhWb9crUNKTpk4FlQ9rjnXqp_6AEQhDhDQVa1nvWCHEvnpvSEAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMnG8KNcZLIjEnlRPxAgEQgDsGPIcKjsMU7Qel36BtM5FCQa56mYUy24_AAoqd12yjVFkq6egLqS0inp-5G4JE7frJURMV8VTw_fY14g:rYyABXmXsBhEdYU_dfmDH8GF-vzSseJXE5bds_zAyVdAwlTXOPdTl9i4PS4jVofLvpq7IRgvEt2BzGhR6cWiQXHHD0AtayQOkZm262ClFCsMYtKGfep0Jq1n0eiRIVqT9gAY7rWvhpEDF8oAlgnRGmqx-euIkpzE79sPXh4OjNx_yUIaanjyplGpBBJ_NiACd1sMlZmseyUyyU_gldqlCBAuK9hhATc9icgg7zuoc7nKBBrlg49tRtzEagDh6xbFBpzfCp7kE_7n4TLWfWHLPMzu-bktjwA969G9sFRmoH1GjPA0a2odDT4dk1_1ouBrB1NcTT2hQUqH-_RzDW2nWmeoVHA\",\n \"provider_refresh_token\": \"5034113:19555731:87c14258f0c813d02767ee975f6044d46b6b2bfc\",\n \"expires\": 1779091997,\n \"refresh_token_expires\": null,\n \"provider\": \"pipedrive\",\n \"state\": \"connected\",\n \"auth_scope\": \"base,deals:full,activities:full,contacts:full,search:read\",\n \"retry_after\": null,\n \"created_at\": \"2025-04-16 08:23:28\",\n \"updated_at\": \"2026-05-18 07:13:18\",\n \"provider_user_token_encrypted\": \"eyJpdiI6Ik9oYzBwcTVteWN4ajBRaDdMb0FjWGc9PSIsInZhbHVlIjoiQUNMbjU5UHR2Q1RnbWRXMEU1TUx6UVpRU3gzdVc5WktmTE4rVlhXa3lRaGhyUUlZMVZRWEJuR0JaWWhZZXNPRlBuanhuYU5MVHZ5TG9NTVVjNjJSUUpZZ2VhbGYxbHg0UzJEdTVmQjNFZnJRZkMwYjd0N2RMZ05GRS9IR3c2K0NoVjBjYTA5UG12SkxHeDlDMGVNN3ptUWUzblFiTngyYzR1eUpHSEYrdEwvZm0yZklGYjBSNEtTR2ZGazhLM1hqT3lVbjFobDNPZ0d3anFlcjhKcXpERlZGOVN1YmFyNDIvM1BFRDFMYnp4WEI4TGVLM2xYcy9CL0RDOWlQNHI3N0NwWnJPMWRtZllmM2ZKeE9TV2NBeDZxL2M1YnlSVzd3ZW96T3F1QmtBYXBHYkUraGcvdCtRajlYZXBjVVBSdFlMUjVPVDVJbDdyenVoWjMrbVJvU3ZZR1dQZ3pqQ3F3aDF2U2xPZWZIN3BCWVFLNXpxQUtDb2pIK0xHc1E3SklSM3Q0VkNSbm9oK2pFK3hLaW54T1dWbEVneUp0RGhhdVBuOFI4MXROc3dZWnFnTUpiaDVMMnZ4QmlRM3k4QWFlMVFWUFYvdGhJQzZBYnhBQmhWa2ZKN1ZhSnhpaExxbmlTemgzUWw3aXJtWjlSMlhmd0lWMDNDN1N3My9Ua0hCL2xqY3RkVFhMSjFJMDRjOE5DWlgrZ3FMVXN6RWlwUE5GWERZdG1xVjVxOFlLRGs2VVJKS3FLeWRxQjYxdDh4Z2JJNXhlWEZ3dkQ4SGtybDNUcndzblFHeVJNRkYraFh2UDFIUTdMQ1BZa3dEU1dBbzk5K0dyT2RNVFBZZUJpRytSck5pYlI1YUZyMmhUNEZCdWxHYmJLREtzbUpjVkhvV0RoejJpbm1SWHlNaXE5M0RhcS94UW9EdFoweWF3bVFVY0dTZWFPSXBmOCtDYndia215cmtyZU1oT0Exam1CV2tPblNhYjY4clVEeUs3anVHUmNHeS9YLzRha1VhbGl3N3lwaDlzZnRhQUZta2s4eFllNHgzRklSRmNyazJ4dlBDMnByNCtBRjNaVjJCTT0iLCJtYWMiOiJkYTQxZDY1OTY2ZTljMTgyZWRhNGEzMGZjZDc2MjBjN2NlNzU2YTViNGQxNWE1NTI2ZmI1MWQyYjQ2ODYxZmEwIiwidGFnIjoiIn0=\",\n \"provider_refresh_token_encrypted\": \"eyJpdiI6Imt2bGxlSmxUK05GMjF0dEtPaTMrUlE9PSIsInZhbHVlIjoiUXZHTElZRXkreHFxUU02SnZ4eVBacG9WWTRlVkd0USszV3JyVFlpU2ZaY25GSWo2WFcrRzNlbFRlUXRQczNwSXRCcEdvZEpveG9jMzBDSTgwdnZLQnc9PSIsIm1hYyI6ImQxYWI0NzU5Nzg5MDI4YWVhZmQ4Mjg1YzhkZDQzMjRkYWYwYTdhZTY5MDMxMjc0OWNiYjY0Nzc3NDQ0MTEyY2EiLCJ0YWciOiIifQ==\",\n \"encryption_key\": \"0x01020300786192D9D96DD60D3D1EBC9DFAEE5F0C45EE80165CF3F3D2B3B627026AA7CFC7CA0128DD463535D32EE491ADBADF8B07596B0000006E306C06092A864886F70D010706A05F305D020100305806092A864886F70D010701301E060960864801650304012E3011040C7FB1319048ECB2EA3F23B995020110802B52D22F5EAD341AB238FBBCB6093156E443876A664BA5555D722565C2B2D04422C868CD7350555E3CC74221\",\n \"sociable_type\": \"user\",\n \"owner_id\": 23460\n },\n {\n \"user_id\": \"23463\",\n \"email\": \"jiminny_web_sa@pipedrive.jiminny.com\",\n \"id\": 72,\n \"sociable_id\": 23463,\n \"provider_user_id\": \"23270841\",\n \"provider_user_token\": \"v1u:AQIBAHj-LzTNK2yuuuaLqifzhWb9crUNKTpk4FlQ9rjnXqp_6AEQhDhDQVa1nvWCHEvnpvSEAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMnG8KNcZLIjEnlRPxAgEQgDsGPIcKjsMU7Qel36BtM5FCQa56mYUy24_AAoqd12yjVFkq6egLqS0inp-5G4JE7frJURMV8VTw_fY14g:txqCuWLeVhgwiaFqXIvQWWrtrnFIy_4lT3VCr-sRB4g6_2lK8KcQ_6ka_qFmZhr-IeIAKmAImx6H1iIKx4Ab7XZ5MnKIh03GKbHjI0n0rGT9UYkeH21jKOxJvKaSX_TyLbxFPR6irPQyvqcpBClXI52gVJH01KzZy_dMTHFXhhDLOIhEpSrQXHG7Qo8q7OuBeSPZrU1ALi9kmAm4GTpzGTbzfl-hUt9IAfPunJOCyv3mf2Tl-MT8EyQgOFPrsP3yz9UlSyVhb40zO_bCnUrqNxjwgm_E6vgpVrwULTQbHe-43Dq-TRetjceUzT88GBgVJ0UdxXP5BRBVXcL5ir8-9ZTMaNU\",\n \"provider_refresh_token\": \"5034113:23270841:34790b44838f5fd422c2d2c82e00f1ecf2178ad1\",\n \"expires\": 1753837219,\n \"refresh_token_expires\": null,\n \"provider\": \"pipedrive\",\n \"state\": \"full-refresh\",\n \"auth_scope\": \"base,deals:full,activities:full,contacts:full,search:read\",\n \"retry_after\": null,\n \"created_at\": \"2025-04-16 10:41:12\",\n \"updated_at\": \"2025-07-30 01:00:17\",\n \"provider_user_token_encrypted\": \"eyJpdiI6InVEVEl2SzIxMjd1bkNDWG9lY3YvQ0E9PSIsInZhbHVlIjoiRlJLbDZ3ZHo2dHMxUWQwdGU2YUZjV3lPWHlxQTJ0eWh4S0RTMjlSaThVcktoaWhXcTd2UXl4Mjg4bW5UUnZCZEQ5NXpuQTVKOURiMDNIa3l3K0lSWCt5azRBOEdHbitWQXhxeEpMVUZ1dnF2NVl1TTZLelZOaGNvRlBLeGpIWlpoRGloUEprd2xoNUFPcTlLcUd3WVlUU2svR1NOeXBYQ2NnMWhnK1V2aytOeVVMNzJGdVZXMU1JS3BSaGNCcmxkVjEwQ01mQXNBdWNhS1lMZWZobnZwcHBkN1R4bUVDRUNYdnNtSVJkdG5MdkN3djJBT0hMRDl1MHFGbnl4WU13ejBkUWsrdUljWkZhcTJlN0JDSGdTVzlPY0FGMUJlZi9WRGpkMUk5R20wblYwSk9VR2FuaHpLTzJNeitMbnF1V1NEMUUxRmYzaDRGZDMyaXd3Z0FjQno4SzVoYjUyTmV3UXNpRlF4TGZKNVl3dUFqdGhKVWJjRVlUUXRGalBpaXJvSGJmbS8rd25aQ2Zvc05saHRDRjlHV1VEUFFiRU5xbWRVNkxFSXV0RUZyaUlYNWMwMmhnZ0Y1VUQ4eEFkY2QyWXpnR0NidkVJSWdjVGljUjA3NDdpZW9WUHhtSlpGSGdNU2hRUTA5ZW03RGpiRFdrdytkOHJXSUxlazZNaG9iaUg2NFZYdEc3YklMeFZvblBzWmVRSDcvN2M4WUNtL3h4S2IrUW00cXhialBia3BJcW5XallBNlV5WWdhcE1ZVFBBM3RSSUVBamYvOGZEQWM5a2FJMGwyWkRSU3dlTEtPemZlQ3RXRGNDcEdYRHdMNTlTQVg2SUdUN0hVaXdZT1dUOUlrVzkrZHFBemhwWXg1cm4rSy9UOHUzcnNSM2dISlhBRTEyVUNyTkZYaFJYUlN1a2RKVHhhM2dHckR3a1JGaVZ2Wk9BVnhTclVpSEhwWTVpQTlJbXVkOUxSTGpRbDk0a1VtbE9QMVAvN3VlVEhJUHd2elowd0laNVIzZ3M3N0ZOK0NDakIwQ2FicGxoMDBhTXI0VUppUmFOZHdlWUdGV21NNFQ4RU1nY0dnWT0iLCJtYWMiOiI0ZTU4ZmJkNTdkYmY0NGE4NTJjZjlhNDExNzE2YjhlOWM2NTA5OGY2MDA4OTcyZDVlOTljY2JjZDhmMjBhMDUyIiwidGFnIjoiIn0=\",\n \"provider_refresh_token_encrypted\": \"eyJpdiI6IkJGMkdRVHRKM2VTcitKNGcvQWJtUGc9PSIsInZhbHVlIjoicmFyRkxPZ1Rybm1ORXlEVjJ2TXFGTzJtM3hWaFhnUVVHN2ZlR1lRM0I5d3ozbnZTcU5EdzJqRkI2elhyNWhlenRTUXV3bjk0N1JXeklDMVAyUzBKcHc9PSIsIm1hYyI6ImJhODVjMGQyZDE3Y2ExNjc2OWVjYWJiNmFjYWVkODIyMTMyNWVhYTExMTgwYTEyMTU0MzUzZGE1YjQ5YzQ5ZjEiLCJ0YWciOiIifQ==\",\n \"encryption_key\": \"0x01020300788C37CDF66ABE9301A68D5D4866AC0C197E04EEE4D904F20A59991322EBAE252301BEF4B24FF01359E934E5654617E4EEAC0000006E306C06092A864886F70D010706A05F305D020100305806092A864886F70D010701301E060960864801650304012E3011040CEB00EF45BDEA999A5558889E020110802B925F372F7BEFAF0B45E48686113D1DA430ECFCC07B1F61BA6828CC44235278EDB05C486E8068D0BE737D91\",\n \"sociable_type\": \"user\",\n \"owner_id\": 23460\n }\n]","depth":4,"on_screen":true,"value":"SELECT * FROM teams WHERE id = 1;\n\nselect * from crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 283;\nSELECT * FROM crm_fields WHERE id = 2234;\nSELECT * FROM crm_field_values WHERE crm_field_id = 2234;\n\nselect * from crm_profiles where user_id = 143;\n\nselect * from record_types where crm_configuration_id = 39; # 0121K000001MHElQAO,0121K000001MHEqQAO\nselect * from business_processes where crm_configuration_id = 39;\n# 01941000000H669AAC, 01941000000H66JAAS\n\nselect * from record_type_field_values\n where record_type_id IN (24);\n\nselect * from crm_field_values where id IN (2730);\n\nselect * from crm_configurations where id = 39;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce'; #1035\n\n\nselect * from users where team_id = 1; # 222 group 3\nSELECT * FROM activities WHERE user_id = 222 order by id desc;\nselect * from sidekick_settings where team_id = 1;\nselect * from teams where id = 1;\nselect * from team_features where team_id = 1;\n\nselect * from activities where crm_configuration_id = 2\nand provider = 'ms-teams' and id = 608765;\n\nSELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id = '59523413338';\n\nselect * from sidekick_settings where team_id = 2;\n\nSELECT * FROM activities WHERE id = 608660;\nselect * from activity_summary_logs where activity_id = 608660;\nselect * from ai_prompts where transcription_id = 11214;\n\n# ********************************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('ed78a437-2804-450e-ab2f-56ab1c641346') = uuid;\n# id: 608818, crm: 59628809737\nSELECT * FROM activities WHERE uuid_to_bin('36b06e55-afdd-4782-8dee-c624cd0af191') = uuid;\n# id: 608821, crm: 59632069252\nSELECT ce.start_time, ce.end_time, a.id, a.uuid, crm_provider_id, calendar_event_id, title,\nplaybook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id,\nscheduled_start_time, scheduled_end_time, actual_start_time, actual_end_time, a.created_at\nFROM activities a\njoin calendar_events ce on a.calendar_event_id = ce.id\nWHERE a.id IN (608818, 608821);\n\nselect * from users where team_id = 1;\nselect * from team_settings where team_id = 1;\nselect * from crm_profiles where crm_configuration_id = 39 order by user_id;\n\nselect * from team_features where team_id = 1;\n\nselect * from users where team_id = 2;\n\nSELECT * FROM activities WHERE uuid_to_bin('ec7647e9-5225-458b-b475-f31aa2769204') = uuid; # 612639\n# Preslava N. Ivanova, grou id 3\n\nSELECT * FROM opportunities WHERE uuid_to_bin('a2928fe5-aec5-46cb-85d9-7654c89e46a6') = uuid;\n\nselect * from activities where opportunity_id = 344 and actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00';\n\nselect\n a.id,\n a.type,\n a.scheduled_start_time,\n a.actual_start_time,\n a.created_at,\n a.opportunity_id,\n a.status\nFROM activities a\nWHERE opportunity_id = 344\nand status IN ('completed', 'received', 'delivered')\nand (\n (a.actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')\nOR (a.created_at between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')\nOR (a.scheduled_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00'))\n;\n\nSELECT * FROM users WHERE id = 222;\n\nSELECT * FROM crm_profiles WHERE user_id = 222;\nselect * from crm_layouts where crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 281;\n\nselect * from group_deal_risk_types;\n\nselect * from opportunities where team_id = 1;\n\nSELECT * FROM opportunities WHERE id = 315;\nSELECT * FROM crm_field_data WHERE object_id = 315;\nselect * from crm_field_data where object_id = 260;\n\nselect * from generic_ai_prompts where subject_id = 315;\n\nselect * from teams; # 36, 21, 121, james.graham@bullhorn.jiminny.com\nSELECT * FROM social_accounts WHERE sociable_id = 121 and provider = 'bullhorn';\n\n# ************************************************************************************\nselect * from teams where id = 1;\nselect * from crm_configurations where id = 39;\nselect * from users where team_id = 1;\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 1;\n# 1 - 00541000004281rAAA\n# 204 - 0052g000003freeAAA\n# 429 - 0052g000003qGOiAAM\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce';\n\nselect * from activities where type = 'softphone'\nand created_at > '2024-12-11 15:24:36' order by id desc;\n\nselect * from activity_providers where team_id = 1;\nselect * from activity_provider_users where activity_provider_id = 328;\n\nselect * from opportunities where crm_configuration_id = 39\nAND account_id = 178 AND is_closed = false\norder by created_at DESC;\n\nselect * from contacts where id = 3952;\nselect * from accounts where id = 178;\n\n# ************************************************************************************\nselect * from teams where id = 36;\nselect * from crm_configurations where id = 21;\nselect * from users where team_id = 36;\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 36;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 36\nand sa.provider = 'bullhorn';\n\nselect * from social_accounts where id = 348;\nUPDATE social_accounts SET\nprovider_user_token = '21442_6802599_91:41179a58-21e7-4d7c-ad58-56bb666b2f65',\nprovider_refresh_token = '21442_6802599_91:01c6b335-3f2a-42e4-85ff-8a08fa65fceb',\nexpires = 1733998131,\nstate = 'connected'\nWHERE id = 348;\n\n# ************************************************************************************\nselect * from teams where id = 31;\nselect * from crm_configurations where id = 18;\n\nselect * from users where team_id = 31; # 257\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 31;\n\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 31\nand sa.provider = 'close';\n\nselect * from contacts where crm_configuration_id = 18;\n\n# ********************** NEPTUNE **************************************************************\nselect * from teams;\nselect * from users where id IN (1030, 1035, 1052);\nselect * from crm_configurations;\n\nselect * from users where team_id = 65; # 257\nselect * from team_settings where team_id = 65; # 257\nselect * from invitations where team_id = 65; # 257\nselect * from users where email = 'integration-account@jiminny.com'; # 257\nselect u.email, cp.* from users u\njoin crm_profiles cp on u.id = cp.user_id\nwhere u.team_id = 65;\n\nselect * from crm_configurations where id = 53;\nselect * from accounts where crm_configuration_id = 53 order by id desc;\nselect * from leads where crm_configuration_id = 53 order by id desc;\nselect * from contacts where crm_configuration_id = 53 order by id desc;\nselect * from opportunities where crm_configuration_id = 53 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 53 order by id desc;\nselect * from crm_fields where crm_configuration_id = 53 order by id desc;\nselect * from crm_field_values where crm_field_id = 3341 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 53 order by id desc;\nselect * from stages where crm_configuration_id = 53 order by id desc;\n\n\nselect * from crm_profiles where crm_configuration_id = 13;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 65\nand sa.provider = 'integration-app';\n\nselect * from contacts where crm_configuration_id = 13;\n\nselect * from social_accounts where sociable_id = 283;\n\nSELECT * FROM opportunities WHERE crm_provider_id = '006O400000E9bzeIAB';\n\nselect * from activity_providers where team_id = 65;\nSELECT * FROM activities WHERE crm_configuration_id IN (51, 52, 53);\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 65\n;\n\n# ***************************** STAGING ********************************************\nSELECT * FROM teams;\nSELECT * FROM teams WHERE id = 88;\nSELECT * FROM teams WHERE id = 89;\nselect * from team_settings where team_id = 89;\nSELECT * FROM users WHERE team_id = 89;\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 89;\n\nselect * from users;\nSELECT * FROM social_accounts WHERE sociable_id = 1761;\nSELECT * FROM crm_configurations WHERE id = 70;\nselect * from accounts where crm_configuration_id = 70 order by id desc;\nselect * from leads where crm_configuration_id = 70 order by id desc;\nselect * from contacts where crm_configuration_id = 70 order by id desc;\nselect * from opportunities where crm_configuration_id = 70 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 70 order by id desc;\nselect * from crm_fields where crm_configuration_id = 70 order by id desc;\nselect * from crm_field_values where crm_field_id = 3536 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 70 order by id desc;\nselect * from stages where crm_configuration_id = 70 order by id desc;\nselect * from business_processes where crm_configuration_id = 70 order by id desc;\nselect * from business_process_stages where business_process_id = 34;\n\nselect * from contacts where id = 10468;\n\nselect * from crm_layouts where crm_configuration_id = 70;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 388;\nSELECT * FROM crm_fields WHERE id IN (3533,3534,3535);\n\nselect * from activities where crm_configuration_id = 70\nand (account_id IS NOT NULL or lead_id IS NOT NULL or contact_id IS NOT NULL or opportunity_id IS NOT NULL) order by id desc;\n\nSELECT * FROM activities WHERE uuid_to_bin('2e10b60f-8a61-41c5-a3d4-28835353dc65') = uuid;\nSELECT * FROM activities where crm_configuration_id = 69 ;\n\nSELECT * FROM users WHERE email LIKE '%jiminny_web_sa2@jiminny.com%';\nSELECT * FROM activities WHERE uuid_to_bin('5a150c93-40fc-42ec-b3bd-c1d328e09f6e') = uuid;\nSELECT * FROM opportunities WHERE id = 385;\n\nselect * from participants p\njoin activities a on p.activity_id = a.id\nwhere a.crm_configuration_id = 70\nand (p.lead_id IS NOT NULL or p.contact_id IS NOT NULL);\nSELECT * FROM participants WHERE id = 1013638;\n\nselect * from teams where id = 90;\nselect * from users where team_id = 90;\nselect * from social_accounts where social_accounts.sociable_id IN (1960,1760);\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 71;\nselect * from invitations where team_id = 90;\n\nselect * from crm_configurations where id = 71;\nselect * from accounts where crm_configuration_id = 71 order by id desc;\nselect * from leads where crm_configuration_id = 71 order by id desc;\nselect * from contacts where crm_configuration_id = 71 order by id desc;\nselect * from opportunities where crm_configuration_id = 71 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 71 order by id desc;\nselect * from crm_fields where crm_configuration_id = 71 order by id desc;\nselect * from crm_field_values where crm_field_id = 3341 order by id desc;\nselect * from crm_layouts where crm_configuration_id = 71 order by id desc;\nselect * from stages where crm_configuration_id = 71 order by id desc;\n\nselect * from users order by secondary_email desc;\nselect u.id, u.email, u.status, sa.id, sa.provider_user_id from social_accounts sa\n join users u on sa.sociable_id = u.id\nwhere sa.provider = 'google' and u.email LIKE 'aneliya%';\n\nselect * from failed_jobs order by id desc;\n\nselect * from users where email = 'ben.allwright@learningpeople.co.uk' or secondary_email = 'ben.allwright@learningpeople.co.uk';\n\nselect * from teams;\nSELECT * FROM crm_profiles WHERE crm_configuration_id = 39;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type = 'task';\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 1\nand sa.provider = 'salesforce';\n\n# ************************************************************************************\nSELECT * FROM activities WHERE uuid_to_bin('c38b3895-fd0f-4b1f-9fb2-c170dba137c6') = uuid;\nSELECT * FROM crm_configurations WHERE id = 70;\n\nselect * from teams where id = 1;\nselect * from groups where team_id = 1;\nselect * from users where team_id = 1;\n\nselect o.id, o.name,o.close_date, u.id, u.name, u.group_id, r.id, r.display_name, g.name, g.scope from opportunities o\njoin users u on o.user_id = u.id\njoin groups g on u.group_id = g.id\njoin role_user ru on u.id = ru.user_id\njoin roles r on ru.role_id = r.id\nwhere o.crm_configuration_id = 39 and close_date > '2024-01-01 00:00:00';\n\nselect * from role_user where user_id = 143;\nselect * from roles;\n\nselect * from role_user;\nselect * from groups where id = 9;\nselect * from scope_groups where group_id = 9;\n\n# ************************************************************************************\nselect * from teams where id = 36;\nselect * from crm_configurations;\nSELECT * FROM social_accounts WHERE sociable_id = 121;\n\nhttps://crmsandbox.zoho.com/crm/jiminnyw4/tab/Leads/4776201000005049105\nhttps://crmsandbox.zoho.com/crm/\n\nhttps://crm.zoho.com/crm/org3469620/tab/Leads/230045000229559080\n https://crm.zoho.com/crm/\n org3469620\n\nSELECT * FROM activities WHERE uuid_to_bin('03382d20-c8bc-48e7-a3d4-90b52fa5ceab') = uuid;\n\nselect * from users where email LIKE \"%mobile_automation_%\";\nselect * from social_accounts where sociable_id IN (2228);\nselect * from crm_profiles where user_id IN (2222,2223,2226,2227);\n\nselect * from teams order by id desc;\nSELECT * FROM users WHERE id = 2229;\nSELECT * FROM crm_profiles WHERE user_id = 2229;\nselect * from opportunities where crm_configuration_id = 88;\nselect * from crm_fields where crm_configuration_id = 88;\nselect * from crm_profiles where crm_configuration_id = 88;\n\nSELECT * FROM teams WHERE id = 1;\n\nSELECT * FROM users WHERE id = 143;\nSELECT * FROM users WHERE uuid_to_bin('fde193d3-06a2-4e1a-8895-62b94039215d') = uuid;\nSELECT * FROM teams WHERE uuid_to_bin('73385071-a756-42ae-9c73-8b53f2309467') = uuid;\n\nhttps://app.staging.jiminny.com/ondemand?\n min_duration=1\n &\n only_recorded=1\n &\n user_id%5B%5D=641f1acb-16b8-42d1-8726-df52979dad0e\n &\n sequence_number=2\n\n select * from users where team_id = 1 and email like '%stoyan%'\n\nselect * from coaching_feedbacks;\n\nselect * from teams;\nSELECT * FROM users WHERE team_id = 36;\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 19\nand sa.provider = 'pipedrive';\n\nselect * from users where id = 143;\n\nSELECT * FROM users WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\nSELECT * FROM teams WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\nSELECT * FROM activity_shares WHERE uuid_to_bin('73180eeb-33de-4065-977d-ccbe0e6c94fc') = uuid;\n\nselect * from users where team_id = 2;\nselect * from activities where crm_configuration_id = 39\nand activities.scheduled_start_time BETWEEN '2025-04-09 00:00:00' AND '2025-04-09 23:59:59'\nAND user_id = 143\norder by id desc;\n\n# ************************************************************************************\nselect * from teams where id = 142; # 2312, 126\nselect * from team_settings;\nselect * from users where team_id = 142; # 21642\nSELECT * FROM social_accounts WHERE sociable_id = 21642;\nSELECT * FROM crm_profiles cp join users u ON u.id = cp.user_id WHERE team_id = 142;\nselect * from crm_profiles where id IN (93);\nselect * from invitations;\nselect * from team_features where team_id = 1;\n\nSELECT * FROM crm_configurations WHERE id = 126;\nselect * from accounts where crm_configuration_id = 126 order by id desc;\nselect * from leads where crm_configuration_id = 126 order by id desc;\nselect * from contacts where crm_configuration_id = 126 order by id desc;\nselect * from opportunities where crm_configuration_id = 126 order by id desc;\nselect * from crm_profiles where crm_configuration_id = 126 order by id desc;\nselect * from crm_fields where crm_configuration_id = 126 # 11060\n# and type IN ('picklist', 'status')\n# and object_type = 'task'\norder by id desc;\n# 5731,5732,5733\nselect DISTINCT crm_field_id from crm_field_values where crm_field_id IN (11151,12239,12215,12185,12175,12165,12144,12137,12127,12109,12107,12105,12103,12092,12037,12005,12003,11987,11969,11958,11951,11942,11931,11924,11921,11917,11915,11901,11893,11883,11872,11870,11868,11866,11839,11833,11821,11793,11780,11777,11769,11757,11737,11735,11656,11645,11638,11629,11618,11611,11602,11591,11584,11581,11558,11544,11543,11534,11532,11529,11527,11503,11497,11493,11488,11470,11468,11457,11455,11397,11387,11372,11363,11348,11323,11318,11309,11301,11300,11292,11290,11286,11284,11256,11252,11242,11237,11233,11219,11176,11160) order by id desc;\nselect * from crm_layouts where crm_configuration_id = 126 order by id desc;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id in (300,299,298);\nselect * from stages where crm_configuration_id = 126 order by id desc;\nselect * from business_processes where crm_configuration_id = 126 order by id desc;\nselect * from business_process_stages where business_process_id IN (76,75,74,73);\nselect * from playbooks where team_id = 142;\nselect * from playbook_layouts where playbook_id IN (108);\nSELECT * FROM playbook_categories WHERE playbook_id IN (108);\n\nselect * from teams where id = 130;\nSELECT * FROM social_accounts WHERE sociable_id = 2291;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 2\nand sa.provider = 'hubspot';\n\nSELECT * FROM activities\n WHERE crm_configuration_id = 110;\n\nselect * from teams;\nselect * from crm_configurations;\n\nSELECT * FROM activities WHERE id = 628773;\nSELECT * FROM crm_profiles WHERE user_id = 1460;\nSELECT * FROM social_accounts WHERE sociable_id = 2291;\n\nselect * from teams;\nselect ru.*, pr.*, p.* from users u join role_user ru on ru.user_id = u.id\njoin permission_role pr on pr.role_id = ru.role_id\n join permissions p on p.id = pr.permission_id\nwhere team_id = 495 and p.name IN ('dial');\n\nselect * from teams where id = 145;\nselect * from crm_configurations where id = 129;\nselect * from social_accounts where sociable_id = 2317;\nSELECT * FROM activities WHERE uuid_to_bin('8dbab184-a333-4268-ad57-fb41f8d53a9a') = uuid;\n\nselect * from teams where id = 1;\nSELECT * FROM crm_layouts WHERE crm_configuration_id = 39;\nSELECT * FROM crm_layout_entities WHERE crm_layout_id = 280;\nSELECT * FROM crm_layout_entities WHERE id = 5507;\nSELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type IN ('event');\n\nselect * from teams;\nselect * from activities where crm_configuration_id = 14;\n\nSELECT * FROM social_accounts where provider = 'copper';\n\nselect * from activities where id = 628467;\nselect * from participants where activity_id = 628467;\n\nSELECT * FROM contacts WHERE id = 3969;\nSELECT * FROM accounts WHERE id = 177;\n\nSELECT * FROM activities WHERE uuid_to_bin('4eb54c77-cfa3-2bd4-84a7-9ed46a21c988') = uuid;\n\n# ********************* BH\nselect * from teams where id = 36;\nSELECT * FROM crm_configurations WHERE id = 21;\nselect * from activities where crm_configuration_id = 21 and id = 607901;\nselect * from activities where crm_configuration_id = 21;\n\nselect * roles;\nselect * from permissions;\nselect * from permission_role where permission_id = 226;\n\nselect * from migrations order by id desc;\n\n# mercury\n# neptune\n# earth\n\nselect * from teams;\nselect * from teams where id = 19;\nselect * from teams where id = 27;\nselect * from users where team_id = 27;\nSELECT * FROM crm_configurations WHERE id = 42;\n\nselect * from social_accounts sa\njoin users u on sa.sociable_id = u.id\nwhere u.team_id = 19\nand sa.provider = 'pipedrive';\n\nselect * from activities where id = 631461;\nSELECT * FROM crm_field_values WHERE crm_field_id = 180;\n\nselect * from teams where id = 2;\nSELECT * FROM social_accounts WHERE sociable_id = 89;\n\nSELECT * FROM activities WHERE uuid_to_bin('ba0c029a-bc14-4e17-8603-64174acebcbb') = uuid; # 634273\nselect * from activity_summary_logs where activity_id = 634273;\n\nselect * from sidekick_settings where team_id = 2;\n\nselect * from teams; # 2, 2\nSELECT * FROM crm_configurations WHERE team_id = 2; # 2\nselect * from team_features where team_id = 2;\nselect * from features;\nSELECT * FROM opportunities WHERE crm_configuration_id = 2 and crm_provider_id = '51317301383';\nSELECT * FROM opportunities WHERE crm_configuration_id = 2 order by id desc;\n\nselect * from automated_reports order by id desc;\nselect * from automated_report_results order by id desc;\nselect * from users where team_id = 1 and id IN (7160, 3248);\nselect * from migrations order by id desc;\n\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1052 and sa.provider = 'hubspot';\n\nselect * from teams where id = 1;\nselect * from groups g JOIN playbooks p on g.playbook_id = p.id where g.team_id = 1;\nselect * from groups where id = 565;\nselect * from playbooks where team_id = 1;\nselect * from playbooks where id = 175;\nselect * from playbook_categories where playbook_id = 175;\nselect * from users where team_id = 1052;\nselect * from users where id = 7160;\nselect * from crm_profiles where user_id = 7160;\nselect * from features;\nselect\n *\n# id, uuid, type, provider, playbook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id, stage_id,\n# crm_configuration_id, crm_provider_id, transcription_id, status\nfrom activities where crm_configuration_id = 1 and type = 'conference'\n# and crm_provider_id IS NOT NULL\nand provider != 'uploader' and actual_start_time IS NOT NULL\nORDER by id desc;\nselect * from activities where id = 54747783; # 00UO400000pCzojMAC\n\nselect p.id, p.activity_type, pc.id, pc.name\nFROM playbooks p\njoin playbook_categories pc on p.id = pc.playbook_id\nwhere p.team_id = 1 and p.activity_type = 'event';\n\nSELECT * FROM crm_fields WHERE crm_configuration_id = 1 and object_type = 'event';\nSELECT * FROM crm_field_values WHERE crm_field_id = 4;\n\nselect * from crm_layouts cl join playbook_layouts pl on cl.id = pl.layout_id\nwhere crm_configuration_id = 1 and pl.playbook_id = 175;\n\nselect * from teams;\nSELECT r.* FROM automated_reports r\njoin teams t on r.team_id = t.id\nWHERE r.frequency = 'daily'\n and r.status = 1\nAND t.status = 'active'\nAND (r.expires_at >= now() OR r.expires_at IS NULL);\n\nselect * from automated_report_results where report_id IN (18, 33);\n\nselect * from users where team_id = 1 and id = 1047;\nSELECT * FROM social_accounts WHERE sociable_id = 1047;\n\nselect * from activity_searches where id = 10932;\nselect * from activity_search_filters where activity_search_id = 10932;\nselect * from automated_reports order by id desc;\nselect * from automated_report_results order by id desc;\nselect * from automated_reports where id IN (55);\nselect * from automated_report_results where id IN (81);\nselect * from users where id IN (10633, 13987, 11985);\nselect * from users where group_id IN (3710);\n\nSELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;\nSELECT * FROM automated_report_results WHERE uuid_to_bin('582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;\n\nselect * from teams;\nselect * from accounts where team_id = 1;\n\nselect * from automated_report_results where media_type = 'pdf' and status = 2;\nSELECT * FROM automated_report_results WHERE uuid_to_bin('82e74956-6144-4cd1-a3d3-af985c3070a4') = uuid;\n\nselect * from teams where id = 1029;\nselect * from crm_configurations where provider = 'pipedrive';\nSELECT\n CONCAT(u.id, CASE WHEN u.id = t.owner_id THEN ' (owner)' ELSE '' END) AS user_id,\n u.email,\n sa.*,\n t.owner_id FROM social_accounts sa\nJOIN users u on u.id = sa.sociable_id\nJOIN teams t on t.id = u.team_id\nWHERE u.team_id = 1029 and sa.provider = 'pipedrive';\n\n[\n {\n \"user_id\": \"23460 (owner)\",\n \"email\": \"integration-account@pipedrive.jiminny.com\",\n \"id\": 69,\n \"sociable_id\": 23460,\n \"provider_user_id\": \"19555731\",\n \"provider_user_token\": \"v1u:AQIBAHj-LzTNK2yuuuaLqifzhWb9crUNKTpk4FlQ9rjnXqp_6AEQhDhDQVa1nvWCHEvnpvSEAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMnG8KNcZLIjEnlRPxAgEQgDsGPIcKjsMU7Qel36BtM5FCQa56mYUy24_AAoqd12yjVFkq6egLqS0inp-5G4JE7frJURMV8VTw_fY14g:rYyABXmXsBhEdYU_dfmDH8GF-vzSseJXE5bds_zAyVdAwlTXOPdTl9i4PS4jVofLvpq7IRgvEt2BzGhR6cWiQXHHD0AtayQOkZm262ClFCsMYtKGfep0Jq1n0eiRIVqT9gAY7rWvhpEDF8oAlgnRGmqx-euIkpzE79sPXh4OjNx_yUIaanjyplGpBBJ_NiACd1sMlZmseyUyyU_gldqlCBAuK9hhATc9icgg7zuoc7nKBBrlg49tRtzEagDh6xbFBpzfCp7kE_7n4TLWfWHLPMzu-bktjwA969G9sFRmoH1GjPA0a2odDT4dk1_1ouBrB1NcTT2hQUqH-_RzDW2nWmeoVHA\",\n \"provider_refresh_token\": \"5034113:19555731:87c14258f0c813d02767ee975f6044d46b6b2bfc\",\n \"expires\": 1779091997,\n \"refresh_token_expires\": null,\n \"provider\": \"pipedrive\",\n \"state\": \"connected\",\n \"auth_scope\": \"base,deals:full,activities:full,contacts:full,search:read\",\n \"retry_after\": null,\n \"created_at\": \"2025-04-16 08:23:28\",\n \"updated_at\": \"2026-05-18 07:13:18\",\n \"provider_user_token_encrypted\": \"eyJpdiI6Ik9oYzBwcTVteWN4ajBRaDdMb0FjWGc9PSIsInZhbHVlIjoiQUNMbjU5UHR2Q1RnbWRXMEU1TUx6UVpRU3gzdVc5WktmTE4rVlhXa3lRaGhyUUlZMVZRWEJuR0JaWWhZZXNPRlBuanhuYU5MVHZ5TG9NTVVjNjJSUUpZZ2VhbGYxbHg0UzJEdTVmQjNFZnJRZkMwYjd0N2RMZ05GRS9IR3c2K0NoVjBjYTA5UG12SkxHeDlDMGVNN3ptUWUzblFiTngyYzR1eUpHSEYrdEwvZm0yZklGYjBSNEtTR2ZGazhLM1hqT3lVbjFobDNPZ0d3anFlcjhKcXpERlZGOVN1YmFyNDIvM1BFRDFMYnp4WEI4TGVLM2xYcy9CL0RDOWlQNHI3N0NwWnJPMWRtZllmM2ZKeE9TV2NBeDZxL2M1YnlSVzd3ZW96T3F1QmtBYXBHYkUraGcvdCtRajlYZXBjVVBSdFlMUjVPVDVJbDdyenVoWjMrbVJvU3ZZR1dQZ3pqQ3F3aDF2U2xPZWZIN3BCWVFLNXpxQUtDb2pIK0xHc1E3SklSM3Q0VkNSbm9oK2pFK3hLaW54T1dWbEVneUp0RGhhdVBuOFI4MXROc3dZWnFnTUpiaDVMMnZ4QmlRM3k4QWFlMVFWUFYvdGhJQzZBYnhBQmhWa2ZKN1ZhSnhpaExxbmlTemgzUWw3aXJtWjlSMlhmd0lWMDNDN1N3My9Ua0hCL2xqY3RkVFhMSjFJMDRjOE5DWlgrZ3FMVXN6RWlwUE5GWERZdG1xVjVxOFlLRGs2VVJKS3FLeWRxQjYxdDh4Z2JJNXhlWEZ3dkQ4SGtybDNUcndzblFHeVJNRkYraFh2UDFIUTdMQ1BZa3dEU1dBbzk5K0dyT2RNVFBZZUJpRytSck5pYlI1YUZyMmhUNEZCdWxHYmJLREtzbUpjVkhvV0RoejJpbm1SWHlNaXE5M0RhcS94UW9EdFoweWF3bVFVY0dTZWFPSXBmOCtDYndia215cmtyZU1oT0Exam1CV2tPblNhYjY4clVEeUs3anVHUmNHeS9YLzRha1VhbGl3N3lwaDlzZnRhQUZta2s4eFllNHgzRklSRmNyazJ4dlBDMnByNCtBRjNaVjJCTT0iLCJtYWMiOiJkYTQxZDY1OTY2ZTljMTgyZWRhNGEzMGZjZDc2MjBjN2NlNzU2YTViNGQxNWE1NTI2ZmI1MWQyYjQ2ODYxZmEwIiwidGFnIjoiIn0=\",\n \"provider_refresh_token_encrypted\": \"eyJpdiI6Imt2bGxlSmxUK05GMjF0dEtPaTMrUlE9PSIsInZhbHVlIjoiUXZHTElZRXkreHFxUU02SnZ4eVBacG9WWTRlVkd0USszV3JyVFlpU2ZaY25GSWo2WFcrRzNlbFRlUXRQczNwSXRCcEdvZEpveG9jMzBDSTgwdnZLQnc9PSIsIm1hYyI6ImQxYWI0NzU5Nzg5MDI4YWVhZmQ4Mjg1YzhkZDQzMjRkYWYwYTdhZTY5MDMxMjc0OWNiYjY0Nzc3NDQ0MTEyY2EiLCJ0YWciOiIifQ==\",\n \"encryption_key\": \"0x01020300786192D9D96DD60D3D1EBC9DFAEE5F0C45EE80165CF3F3D2B3B627026AA7CFC7CA0128DD463535D32EE491ADBADF8B07596B0000006E306C06092A864886F70D010706A05F305D020100305806092A864886F70D010701301E060960864801650304012E3011040C7FB1319048ECB2EA3F23B995020110802B52D22F5EAD341AB238FBBCB6093156E443876A664BA5555D722565C2B2D04422C868CD7350555E3CC74221\",\n \"sociable_type\": \"user\",\n \"owner_id\": 23460\n },\n {\n \"user_id\": \"23463\",\n \"email\": \"jiminny_web_sa@pipedrive.jiminny.com\",\n \"id\": 72,\n \"sociable_id\": 23463,\n \"provider_user_id\": \"23270841\",\n \"provider_user_token\": \"v1u:AQIBAHj-LzTNK2yuuuaLqifzhWb9crUNKTpk4FlQ9rjnXqp_6AEQhDhDQVa1nvWCHEvnpvSEAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMnG8KNcZLIjEnlRPxAgEQgDsGPIcKjsMU7Qel36BtM5FCQa56mYUy24_AAoqd12yjVFkq6egLqS0inp-5G4JE7frJURMV8VTw_fY14g:txqCuWLeVhgwiaFqXIvQWWrtrnFIy_4lT3VCr-sRB4g6_2lK8KcQ_6ka_qFmZhr-IeIAKmAImx6H1iIKx4Ab7XZ5MnKIh03GKbHjI0n0rGT9UYkeH21jKOxJvKaSX_TyLbxFPR6irPQyvqcpBClXI52gVJH01KzZy_dMTHFXhhDLOIhEpSrQXHG7Qo8q7OuBeSPZrU1ALi9kmAm4GTpzGTbzfl-hUt9IAfPunJOCyv3mf2Tl-MT8EyQgOFPrsP3yz9UlSyVhb40zO_bCnUrqNxjwgm_E6vgpVrwULTQbHe-43Dq-TRetjceUzT88GBgVJ0UdxXP5BRBVXcL5ir8-9ZTMaNU\",\n \"provider_refresh_token\": \"5034113:23270841:34790b44838f5fd422c2d2c82e00f1ecf2178ad1\",\n \"expires\": 1753837219,\n \"refresh_token_expires\": null,\n \"provider\": \"pipedrive\",\n \"state\": \"full-refresh\",\n \"auth_scope\": \"base,deals:full,activities:full,contacts:full,search:read\",\n \"retry_after\": null,\n \"created_at\": \"2025-04-16 10:41:12\",\n \"updated_at\": \"2025-07-30 01:00:17\",\n \"provider_user_token_encrypted\": \"eyJpdiI6InVEVEl2SzIxMjd1bkNDWG9lY3YvQ0E9PSIsInZhbHVlIjoiRlJLbDZ3ZHo2dHMxUWQwdGU2YUZjV3lPWHlxQTJ0eWh4S0RTMjlSaThVcktoaWhXcTd2UXl4Mjg4bW5UUnZCZEQ5NXpuQTVKOURiMDNIa3l3K0lSWCt5azRBOEdHbitWQXhxeEpMVUZ1dnF2NVl1TTZLelZOaGNvRlBLeGpIWlpoRGloUEprd2xoNUFPcTlLcUd3WVlUU2svR1NOeXBYQ2NnMWhnK1V2aytOeVVMNzJGdVZXMU1JS3BSaGNCcmxkVjEwQ01mQXNBdWNhS1lMZWZobnZwcHBkN1R4bUVDRUNYdnNtSVJkdG5MdkN3djJBT0hMRDl1MHFGbnl4WU13ejBkUWsrdUljWkZhcTJlN0JDSGdTVzlPY0FGMUJlZi9WRGpkMUk5R20wblYwSk9VR2FuaHpLTzJNeitMbnF1V1NEMUUxRmYzaDRGZDMyaXd3Z0FjQno4SzVoYjUyTmV3UXNpRlF4TGZKNVl3dUFqdGhKVWJjRVlUUXRGalBpaXJvSGJmbS8rd25aQ2Zvc05saHRDRjlHV1VEUFFiRU5xbWRVNkxFSXV0RUZyaUlYNWMwMmhnZ0Y1VUQ4eEFkY2QyWXpnR0NidkVJSWdjVGljUjA3NDdpZW9WUHhtSlpGSGdNU2hRUTA5ZW03RGpiRFdrdytkOHJXSUxlazZNaG9iaUg2NFZYdEc3YklMeFZvblBzWmVRSDcvN2M4WUNtL3h4S2IrUW00cXhialBia3BJcW5XallBNlV5WWdhcE1ZVFBBM3RSSUVBamYvOGZEQWM5a2FJMGwyWkRSU3dlTEtPemZlQ3RXRGNDcEdYRHdMNTlTQVg2SUdUN0hVaXdZT1dUOUlrVzkrZHFBemhwWXg1cm4rSy9UOHUzcnNSM2dISlhBRTEyVUNyTkZYaFJYUlN1a2RKVHhhM2dHckR3a1JGaVZ2Wk9BVnhTclVpSEhwWTVpQTlJbXVkOUxSTGpRbDk0a1VtbE9QMVAvN3VlVEhJUHd2elowd0laNVIzZ3M3N0ZOK0NDakIwQ2FicGxoMDBhTXI0VUppUmFOZHdlWUdGV21NNFQ4RU1nY0dnWT0iLCJtYWMiOiI0ZTU4ZmJkNTdkYmY0NGE4NTJjZjlhNDExNzE2YjhlOWM2NTA5OGY2MDA4OTcyZDVlOTljY2JjZDhmMjBhMDUyIiwidGFnIjoiIn0=\",\n \"provider_refresh_token_encrypted\": \"eyJpdiI6IkJGMkdRVHRKM2VTcitKNGcvQWJtUGc9PSIsInZhbHVlIjoicmFyRkxPZ1Rybm1ORXlEVjJ2TXFGTzJtM3hWaFhnUVVHN2ZlR1lRM0I5d3ozbnZTcU5EdzJqRkI2elhyNWhlenRTUXV3bjk0N1JXeklDMVAyUzBKcHc9PSIsIm1hYyI6ImJhODVjMGQyZDE3Y2ExNjc2OWVjYWJiNmFjYWVkODIyMTMyNWVhYTExMTgwYTEyMTU0MzUzZGE1YjQ5YzQ5ZjEiLCJ0YWciOiIifQ==\",\n \"encryption_key\": \"0x01020300788C37CDF66ABE9301A68D5D4866AC0C197E04EEE4D904F20A59991322EBAE252301BEF4B24FF01359E934E5654617E4EEAC0000006E306C06092A864886F70D010706A05F305D020100305806092A864886F70D010701301E060960864801650304012E3011040CEB00EF45BDEA999A5558889E020110802B925F372F7BEFAF0B45E48686113D1DA430ECFCC07B1F61BA6828CC44235278EDB05C486E8068D0BE737D91\",\n \"sociable_type\": \"user\",\n \"owner_id\": 23460\n }\n]","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}]...
|
7498689834597179426
|
6686367646540542029
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\Twilio\Conference\ConferenceManager;
use Illuminate\Database\Eloquent;
use Illuminate\Support\Str;
use Jiminny\Component\ElasticSearch\Model\Observer;
use Jiminny\Component\Twilio\Conference\ConferenceManager;
use Jiminny\Component\Twilio\Exception\OutOfBoundsException;
use Jiminny\Component\Twilio\Exception\UnexpectedValueException;
use Jiminny\Component\Twilio\Resolver;
use Jiminny\Component\Twilio\VO;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Events;
use Jiminny\Exceptions\LogicException;
use Jiminny\Models;
use Jiminny\Models\Participant\Connection;
use Jiminny\Services\ActivityService;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Crm\CachedCrmServiceDecorator;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Services\Telephony\TwilioClientBuilder;
use Psr\Log\LoggerInterface;
use Twilio\Exceptions\ConfigurationException;
use Twilio\Exceptions\RestException;
use Twilio\Exceptions\TwilioException;
use Twilio\TwiML;
final readonly class SoftPhoneManager implements ConferenceManager
{
public const int COMPLIANCE_PROMPT_TIMEOUT_SECONDS = 20;
public const string COMPLIANCE_PROMPT_ACCEPT_DIGIT = '1';
public function __construct(
private ProviderRegistry $crmProviderRegistry,
private Webhook $urlGenerator,
private ActivityService $activityService,
private LoggerInterface $logger,
private TwilioClientBuilder $twilioClientBuilder,
) {
}
public function create(VO\ConferenceManager\ActivityCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'data' => $data,
'instanceof' => get_class($data),
]);
$activity = Observer::skipEvents(function () use ($data) {
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundCreate) {
$activity = $this->createInboundActivity($data);
} elseif ($data instanceof VO\ConferenceManager\SoftPhoneOutboundCreate) {
$activity = $this->createOutboundActivity($data);
} else {
throw new LogicException(sprintf(
'Unknown softphone implementation: %s',
get_class($data)
));
}
return $activity;
});
$this->logger->debug(__METHOD__ . ': done', [
'instanceof' => get_class($data),
'activity' => $activity->id_string,
]);
return $activity;
}
private function createOutboundActivity(VO\ConferenceManager\SoftPhoneOutboundCreate $data): Models\Activity
{
$startTime = microtime(true);
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
$now = now();
$user = $data->getUser();
$team = $user->getTeam();
$device = $data->getDevice();
$phoneNumber = $data->getPhoneNumber();
$route = $data->getPhoneRoute();
if (! $this->activityService->teamCanDial($team, $phoneNumber)) {
$region = getRegionByNumber($phoneNumber, $user->getCountryCode());
throw new LogicException(sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? 'this number'
));
}
$lead = null;
$account = null;
$contact = null;
$stage = null;
$opportunity = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $user,
'providerSlug' => $team->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
} catch (\Throwable $throwable) {
$this->logger->error('[SoftPhoneManager] Failed to resolve CRM service with fallback', [
'exception' => $throwable,
'user_id' => $user->getId(),
'team_id' => $team->getId(),
]);
$crmService = $this->crmProviderRegistry->get($team->crm->provider);
if (! $user->isCrmRequired()) {
$crmService->setUser($user->team->owner);
} else {
$crmService->setUser($user);
}
}
$prospectId = $data->getProspectId();
try {
if ($prospectId !== null) {
$crmStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM parseRecords', [
'prospect_id' => $prospectId,
'object_type' => $data->getObjectType(),
]);
[$lead, $account, $opportunity, $contact, $stage] = $crmService->parseRecords(
$prospectId,
$data->getObjectType()
);
$this->logger->info('[SoftPhoneManager] CRM parseRecords completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'parse_duration_ms' => round((microtime(true) - $crmStart) * 1000, 2),
'found_lead' => $lead !== null,
'found_account' => $account !== null,
'found_opportunity' => $opportunity !== null,
'found_contact' => $contact !== null,
]);
// Try to attach a contact if we have an opportunity.
if ($opportunity !== null && $contact === null) {
$matchStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting CRM matchByPhone');
[, , , $contact, ] = $crmService->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
$this->logger->info('[SoftPhoneManager] CRM matchByPhone completed', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'match_duration_ms' => round((microtime(true) - $matchStart) * 1000, 2),
'found_contact' => $contact !== null,
]);
}
} else {
$decorator = resolve(CachedCrmServiceDecorator::class);
$decorator->setCrmService($crmService);
[$lead, $account, $opportunity, $contact, $stage] = $decorator->matchByPhone(
$data->getPhoneNumber(),
$data->getRawPhoneNumber(),
$user->getId()
);
}
} catch (\Throwable $throwable) {
$this->logger->error('[' . $crmService->getDisplayName() . '] Error looking up customer', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an outbound call from happening.
}
if ($data->hasProspectStage()) {
/** @var Models\Stage $stage */
$stage = $team->crm->stages()->uuid($data->getProspectStage());
}
$dbStart = microtime(true);
$this->logger->info('[SoftPhoneManager] Starting activity creation in DB');
$activity = $user->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE,
'provider' => Models\Activity::PROVIDER_TWILIO,
'device_id' => $device->id ?? null,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'value' => $opportunity ? $opportunity->value : null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_PENDING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $phoneNumber),
'scheduled_start_time' => $now,
]);
$this->logger->info('[SoftPhoneManager] Activity created in DB', [
'elapsed_ms' => round((microtime(true) - $startTime) * 1000, 2),
'db_duration_ms' => round((microtime(true) - $dbStart) * 1000, 2),
'activity_id' => $activity->id ?? null,
]);
if (! $activity instanceof Models\Activity) {
throw new LogicException('Activity model expected');
}
$regionId = null;
if ($user->hasRegionId()) {
$regionId = $user->getRegionId();
} elseif ($data->hasRegion()) {
$regionId = $data->getRegion()->getId();
}
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $regionId,
'is_muted' => false,
]);
$phoneRouteResolver = new Resolver\PhoneRouteResolver($user, $route);
$phoneNumberOrigin = $phoneRouteResolver->getPhoneNumberOrigin();
$connectionType = $phoneRouteResolver->getConnectionType();
$callerId = $this->determineUserCallerId($user);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'user_id' => $user->id,
'email' => $user->email,
'name' => $user->name,
'phone_number' => $phoneRouteResolver->mustUsePSTN()
? $phoneNumberOrigin
: $callerId,
]);
$caller->connections()->create([
'session_id' => $session->getId(),
'direction' => $phoneRouteResolver->mustUsePSTN() ? 'outbound-api' : 'outbound',
'phone' => $callerId,
'type' => $connectionType,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $phoneNumber,
'name' => $activity->prospect_name,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'phone' => $phoneNumber,
'direction' => 'outbound-api',
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$activity->refresh();
$this->logger->info('[SoftPhoneManager] createOutboundActivity completed', [
'total_duration_ms' => round((microtime(true) - $startTime) * 1000, 2),
'activity_id' => $activity->id_string,
]);
$this->logger->debug(__METHOD__ . ': done', [
'data' => $data,
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
return $activity;
}
private function createInboundActivity(VO\ConferenceManager\SoftPhoneInboundCreate $data): Models\Activity
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$roomOwner = $data->getUser();
$team = $roomOwner->getTeam();
$phoneNumber = $roomOwner->getSoftPhoneInboundDestinationNumber();
$callerId = $data->getCallerId();
$callerCallSid = $data->getCallSid();
$now = now();
if (! $this->activityService->teamCanDial($roomOwner->getTeam(), $roomOwner->softphone_inbound_destination)) {
$region = getRegionByNumber($roomOwner->softphone_inbound_destination, $roomOwner->getCountryCode());
throw new LogicException(
sprintf(
'Sorry, dialing %s is not available with your subscription.'
. ' Please contact your manager to enable additional destinations.',
$region ?? ' this number',
),
);
}
$lead = $account = $opportunity = $contact = $stage = null;
try {
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $roomOwner->getTeam(),
'integrationAdmin' => $roomOwner,
'providerSlug' => $roomOwner->getTeam()->getCrmConfiguration()->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
[$lead, $account, $opportunity, $contact, $stage] = $crmService->matchByPhone(
$callerId,
null,
$roomOwner->getId()
);
} catch (\Throwable $throwable) {
$this->logger->error(__METHOD__ . ': crm exception', [
'exception' => $throwable,
]);
// maybe their crm is disconnected, but this shouldn't prevent an inbound call from happening
}
/** @var Models\Activity $activity */
$activity = $roomOwner->activities()->create([
'type' => Models\Activity::TYPE_SOFTPHONE_INBOUND,
'provider' => Models\Activity::PROVIDER_TWILIO,
'crm_configuration_id' => $team->crm_id,
'lead_id' => $lead->id ?? null,
'account_id' => $account->id ?? null,
'contact_id' => $contact->id ?? null,
'opportunity_id' => $opportunity->id ?? null,
'stage_id' => $stage->id ?? null,
'status' => Models\Activity::STATUS_RINGING,
'recording_state' => Models\Activity::RECORDING_OFF,
'recording_reason_code' => $this->getVoiceRecordingReasonCode($team, $callerId),
'scheduled_start_time' => $now,
]);
/** @var Models\Session $session */
$session = $activity->sessions()->create([
'type' => Models\Session::TYPE_DIALER,
'region_id' => $roomOwner->getRegionId(),
'is_muted' => false,
]);
/** @var Models\Participant $caller */
$caller = $activity->participants()->create([
'lead_id' => $activity->lead_id ?? null,
'contact_id' => $activity->contact_id ?? null,
'phone_number' => $callerId,
'name' => $activity->prospect_name,
'enter_time' => $now,
]);
$caller->connections()->create([
'telephony_provider_id' => $callerCallSid,
'session_id' => $session->getId(),
'phone' => $callerId,
'direction' => 'inbound',
'type' => Connection::TYPE_PSTN,
'opened_at' => $now,
]);
/** @var Models\Participant $callee */
$callee = $activity->participants()->create([
'user_id' => $roomOwner->id,
'email' => $roomOwner->email,
'name' => $roomOwner->name,
'phone_number' => $phoneNumber,
]);
$callee->connections()->create([
'session_id' => $session->getId(),
'direction' => 'outbound-api',
'phone' => $phoneNumber,
'type' => Connection::TYPE_PSTN,
]);
$activity->update([
'from_participant_id' => $caller->id,
'to_participant_id' => $callee->id,
]);
$this->logger->debug(__METHOD__ . ': done', [
'callSid' => $data->getCallSid(),
'activity' => $activity->id_string,
'session' => $session->id_string,
'caller' => $caller->id_string,
'callee' => $callee->id_string,
]);
$activity->refresh();
return $activity;
}
private function getVoiceRecordingReasonCode(Models\Team $team, string $phoneNumber): int
{
$this->logger->debug(__METHOD__, [
'team' => $team->id_string,
'phone' => $phoneNumber,
]);
if (
! $team->hasComplianceModeRecordingPrompt()
&& ! $team->hasComplianceModeRecordingRestrictToOneSide()
&& ! $team->hasComplianceModeRecordingRestrictRecording()
) {
return 0;
}
/** @var Models\VoiceConsentPrefix|null $voiceConsentPolicy */
$voiceConsentPolicy = Models\VoiceConsentPrefix::query()
->whereRaw("? LIKE CONCAT(destination_prefix, '%')", [$phoneNumber])
->where(static function (Eloquent\Builder $builder) use ($team): void {
$builder
->whereNull('team_id')
->orWhere('team_id', $team->getId());
})
->orderByDesc('team_id')
->first();
if (! $voiceConsentPolicy instanceof Models\VoiceConsentPrefix || $voiceConsentPolicy->isAllowed()) {
return 0;
}
if ($team->hasComplianceModeRecordingPrompt()) {
// This number requires consent from the other party before recording the call.
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_PROMPT;
}
if ($team->hasComplianceModeRecordingRestrictToOneSide()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT_ONE_SIDE;
}
if ($team->hasComplianceModeRecordingRestrictRecording()) {
return Models\Activity::FLAG_RECORDING_REASON_COMPLIANCE_RESTRICT;
}
return 0;
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOrganiser(Models\Activity $activity): void
{
$this->logger->debug(__METHOD__, [
'activity' => $activity->id_string,
]);
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$canBeDialed =
$activity->isTypeSoftPhone()
&& ! $activity->hasStarted()
&& $activity->isPending()
&& $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->exists();
if (! $canBeDialed) {
throw new OutOfBoundsException('Cannot make an outbound call to this activity.');
}
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->where('type', '=', Connection::TYPE_PSTN)
->whereNull('opened_at')
->firstOrFail();
/** @var Models\Session $session */
$session = $organiserConnection->getSession();
$twilioConferenceSettings = $this->applyDefaultTwilioParticipantSettings($organiserConnection, [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
]);
// What number will the organizer see as caller id
$callerId = $roomOwner->getSoftPhoneNumber();
if ($callerId === null) {
$callee = $activity->participants()->whereNull('user_id')->first();
// This is a bridge call. We make it look like the callee is calling the organizer
$callerId = $callee->phone_number;
}
// What number will the prospect see as caller id
$calleeId = $organiser->getPhoneNumber();
$this->logger->debug(__METHOD__ . ': twilio call', [
'calleeId' => $calleeId,
'callerId' => $callerId,
]);
$twilioParticipant = $twilio->createParticipantInstance(
$session->id_string,
$callerId,
$calleeId,
$twilioConferenceSettings
);
$organiserConnection
->setTelephonyProviderId($twilioParticipant->callSid)
->save();
$session
->setTelephonyProviderId($twilioParticipant->conferenceSid)
->save();
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'session' => $session->id_string,
]);
}
public function dialCoach(Models\Activity $activity, Models\User $user): void
{
throw new \LogicException('Not implemented yet');
}
/**
* @throws ConfigurationException
* @throws TwilioException
*/
public function terminate(VO\ConferenceManager\ActivityTerminate $data): void
{
if (! $data instanceof VO\ConferenceManager\SoftphoneTerminate) {
throw new UnexpectedValueException(sprintf(
'Expecting an instance of "%s", but "%s" given.',
VO\ConferenceManager\SoftphoneTerminate::class,
\get_class($data)
));
}
$this->logger->debug(__METHOD__, [
'activity' => $data->getActivity()->id_string,
'user' => $data->getUser()->id_string,
]);
$activity = $data->getActivity();
$roomOwner = $activity->getUser();
$isCancelling = ! $activity->isInFiniteState() && ! $activity->isInProgress();
if ($isCancelling) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
}
/** @var Models\Session $session */
$session = $activity->sessions->first();
/** @var Models\Participant $organiser */
$organiser = $activity->organizer;
/** @var Connection $organiserConnection */
$organiserConnection = $organiser->connections()
->whereIn('type', [
Connection::TYPE_WEBRTC,
Connection::TYPE_PSTN,
])
->firstOrFail();
if (! $organiserConnection->hasTelephonyProviderId()) {
throw new LogicException(
'Unable to terminate call as we do not yet know the organiser telephony provider id',
);
}
$twilioClient = $this->twilioClientBuilder->build($activity->getTeam());
$twilioClient->terminateVoiceCall($organiserConnection->getTelephonyProviderId());
$session->hasTelephonyProviderId()
&& $twilioClient->terminateConference($session->getTelephonyProviderId());
$this->logger->debug(__METHOD__ . ': done', [
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'session' => $session->id_string,
]);
}
public function answer(VO\ConferenceManager\ActivityAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'data' => $data,
]);
if ($data instanceof VO\ConferenceManager\SoftPhoneInboundAnswer) {
return $this->answerInboundCall($data);
}
if ($data instanceof VO\ConferenceManager\SoftPhoneOutboundAnswer) {
return $this->answerOutboundCall($data);
}
throw new LogicException('Unable to answer - not supported activity answer instance');
}
private function answerInboundCall(VO\ConferenceManager\SoftPhoneInboundAnswer $data): TwiML\VoiceResponse
{
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
]);
$dateTime = now();
$activity = $this->create(
new VO\ConferenceManager\SoftPhoneInboundCreate(
$data->getCallerId(),
$data->getCallSid(),
$data->getUser()
)
);
/** @var Models\Participant $activityGuest */
$activityGuest = $activity->from;
$activityGuest
->setEnterTime($dateTime)
->save();
/** @var Connection $connection */
$connection = $activityGuest->connections()
->where('type', Connection::TYPE_PSTN)
->firstOrFail();
$connection
->setTelephonyProviderId($data->getCallSid())
->setOpenedAt($dateTime)
->save();
event(new Events\Activities\Connections\Opened($connection));
return $this->joinTwilioConference($connection, [
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]);
}
private function answerOutboundCall(VO\ConferenceManager\SoftPhoneOutboundAnswer $data): TwiML\VoiceResponse
{
$connection = $data->getConnection();
$participant = $connection->getParticipant();
$activity = $data->getActivity();
$organiser = $activity->organizer;
$dateTime = now();
$this->logger->debug(__METHOD__, [
'callSid' => $data->getCallSid(),
'connection' => $connection->id_string,
'participant' => $participant->id_string,
'activity' => $activity->id_string,
]);
if (! $participant->hasEnterTime()) {
$participant->update([
'enter_time' => $dateTime,
]);
}
$connection->update([
'is_muted' => $participant->isCoach(),
'opened_at' => $dateTime,
]);
event(new Events\Activities\Connections\Opened($connection));
if ($participant->isCoach()) {
/** @var Connection $roomOwnerConnection */
$roomOwnerConnection = $organiser->connections()->openCall()->firstOrFail();
return $this->joinTwilioConference(
$connection,
[
'coach' => $roomOwnerConnection->getTelephonyProviderId(),
]
);
}
return $this->joinTwilioConference(
$connection,
[
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
'muted' => false,
]
);
}
private function determineUserCallerId(Models\User $user): string
{
if ($user->hasCallerId()) {
return $user->getCallerId();
}
// TODO: We should require a caller id to be verified before using the dialer
return $user->getSoftPhoneNumber() ?? $user->phone;
}
private function isRestExceptionAboutTwilioConferenceNotFound(string $exceptionMessage): bool
{
$startsWith = '[HTTP 404] Unable to create record: The requested resource';
$endsWith = 'was not found';
return Str::startsWith($exceptionMessage, $startsWith)
&& Str::endsWith($exceptionMessage, $endsWith);
}
/**
* @throws RestException
* @throws ConfigurationException
* @throws TwilioException
*/
public function dialOtherParty(Connection $callerConnection): self
{
$session = $callerConnection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$twilio = $this->twilioClientBuilder->build($activity->getTeam());
$this->logger->debug(__METHOD__, [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
if (! $activity->isTypeSoftPhone() && ! $activity->isTypeSoftphoneInbound()) {
throw new UnexpectedValueException('Unexpected activity type');
}
$callee = $activity->to;
$calleeId = $callee->getPhoneNumber();
/** @var Connection $calleeConnection */
$calleeConnection = $callee->connections()
->whereIn('type', [
Connection::TYPE_PSTN,
Connection::TYPE_WEBRTC,
])
->firstOrFail();
$caller = $activity->from;
// we need to take the number from the connection
// as the one we store on participant level might be the one we call to the rep via PSTN mode
$callerId = $callerConnection->getPhoneNumber();
if (! $caller->is($callerConnection->getParticipant())) {
throw new LogicException(sprintf(
'Unexpected caller participant. Expected: "%s", Actual: "%s"',
$caller->id_string,
$callerConnection->getParticipant()->id_string
));
}
$twilioConferenceSettings = [
'muted' => false,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => true,
];
$hasInboundRecording = $activity->isTypeSoftphoneInbound()
&& $roomOwner->checkSoftphoneInboundRecordPreference();
$hasOutboundRecording = $activity->isTypeSoftPhone()
&& $roomOwner->checkSoftphoneOutboundRecordPreference();
$isRecordingEnabled = $hasInboundRecording || $hasOutboundRecording;
$isRecordingRestricted = $activity->hasRecordingReasonCompliancePrompted()
|| $activity->hasRecordingReasonComplianceRestricted();
if ($isRecordingEnabled && ! $isRecordingRestricted) {
$twilioConferenceSettings = array_merge($twilioConferenceSettings, [
'record' => true,
'recordingChannels' => 'dual',
'recordingStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.recording.event'),
'recordingStatusCallbackMethod' => 'POST',
'recordingStatusCallbackEvent' => 'in-progress completed absent',
]);
if ($activity->hasRecordingReasonComplianceRestrictedToOneSideRecording()) {
$twilioConferenceSettings['recordingTrack'] = $activity->isTypeSoftPhone()
? 'outbound'
: 'inbound';
}
}
try {
if ($activity->hasRecordingReasonCompliancePrompted()) {
$this->logger->info('[Softphone] Request compliance prompt via outbound call', [
'activity_uuid' => $activity->getUuid(),
'session' => $session->id_string,
'conference_sid' => $session->getTelephonyProviderId(),
]);
$callInstance = $twilio->createCall(
$callerId,
$calleeId,
$this->urlGenerator->route('jiminny.webhook.softphone.compliance.prompt', [
'session' => $session->id_string,
'connection' => $calleeConnection->id_string,
]),
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $callInstance->sid,
'parent_connection_id' => $callerConnection->getId(),
]);
} else {
$twilioParticipantConnection = $twilio->createParticipantInstance(
$session->getTelephonyProviderId(),
$callerId,
$calleeId,
$this->applyDefaultTwilioParticipantSettings($calleeConnection, $twilioConferenceSettings)
);
$calleeConnection->update([
'telephony_provider_id' => $twilioParticipantConnection->callSid,
]);
}
} catch (RestException $restException) {
if (! $this->isRestExceptionAboutTwilioConferenceNotFound($restException->getMessage())) {
throw $restException;
}
if (! $activity->isInFiniteState()) {
$activity
->setStatus(Models\Activity::STATUS_CANCELLED)
->save();
$message = __METHOD__ . ': activity.status -> cancelled';
} else {
$message = sprintf(
__METHOD__ . ': not setting to cancelled, activity.status is %s',
$activity->getStatus()
);
}
$this->logger->info($message, [
'activity' => $activity->id_string,
'exception' => $restException,
]);
}
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
'connection' => $callerConnection->id_string,
]);
return $this;
}
private function applyDefaultTwilioParticipantSettings(Connection $connection, array $override = []): array
{
$session = $connection->getSession();
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => true,
'endConferenceOnExit' => false,
'record' => false,
'waitUrl' => '',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]),
'statusCallbackEvent' => [
'initiated',
'ringing',
'answered',
'completed',
],
'statusCallbackMethod' => 'POST',
'conferenceStatusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
]),
'conferenceStatusCallbackMethod' => 'POST',
'conferenceStatusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function applyDefaultTwilioConferenceSettings(Models\Session $session, array $override = []): array
{
$default = [
'beep' => false,
'muted' => true,
'startConferenceOnEnter' => false,
'endConferenceOnExit' => false,
'waitUrl' => '',
'statusCallbackEvent' => implode(' ', [
'start',
'end',
'join',
'leave',
'mute',
'hold',
// 'speaker',
]),
'statusCallbackMethod' => 'POST',
'statusCallback' => $this->urlGenerator->route('jiminny.webhook.softphone.conference.event', [
'session' => $session->getIdString(),
]),
];
if ($session->hasRegion()) {
$default['region'] = $session->getRegion()->getName();
}
return array_merge($default, $override);
}
private function joinTwilioConference(Connection $connection, array $options = []): TwiML\VoiceResponse
{
$session = $connection->getSession();
$activity = $session->getActivity();
$roomOwner = $activity->getUser();
$this->logger->debug(__METHOD__, [
'connection' => $connection->id_string,
'session' => $session->id_string,
'activity' => $activity->id_string,
'user' => $roomOwner->id_string,
]);
$voiceResponse = new TwiML\VoiceResponse();
$voiceResponse->dial()
->setAction($this->urlGenerator->route('jiminny.webhook.softphone.participant.event', [
'connection' => $connection->id_string,
]))
->conference(
$session->getIdString(),
$this->applyDefaultTwilioConferenceSettings($session, $options)
);
$this->logger->debug(__METHOD__ . ': done', [
'session' => $session->id_string,
'result' => $voiceResponse,
]);
return $voiceResponse;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny_jupiter
Sync Changes
Hide This Notification
Code changed:
Hide
19
19
17
Previous Highlighted Error
Next Highlighted Error
SELECT * FROM teams WHERE id = 1;
select * from crm_layouts where crm_configuration_id = 39;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 283;
SELECT * FROM crm_fields WHERE id = 2234;
SELECT * FROM crm_field_values WHERE crm_field_id = 2234;
select * from crm_profiles where user_id = 143;
select * from record_types where crm_configuration_id = 39; # 0121K000001MHElQAO,0121K000001MHEqQAO
select * from business_processes where crm_configuration_id = 39;
# 01941000000H669AAC, 01941000000H66JAAS
select * from record_type_field_values
where record_type_id IN (24);
select * from crm_field_values where id IN (2730);
select * from crm_configurations where id = 39;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce'; #1035
select * from users where team_id = 1; # 222 group 3
SELECT * FROM activities WHERE user_id = 222 order by id desc;
select * from sidekick_settings where team_id = 1;
select * from teams where id = 1;
select * from team_features where team_id = 1;
select * from activities where crm_configuration_id = 2
and provider = 'ms-teams' and id = 608765;
SELECT * FROM activities WHERE crm_configuration_id = 2 and crm_provider_id = '59523413338';
select * from sidekick_settings where team_id = 2;
SELECT * FROM activities WHERE id = 608660;
select * from activity_summary_logs where activity_id = 608660;
select * from ai_prompts where transcription_id = 11214;
# [PASSWORD_DOTS]
SELECT * FROM activities WHERE uuid_to_bin('ed78a437-2804-450e-ab2f-56ab1c641346') = uuid;
# id: 608818, crm: 59628809737
SELECT * FROM activities WHERE uuid_to_bin('36b06e55-afdd-4782-8dee-c624cd0af191') = uuid;
# id: 608821, crm: 59632069252
SELECT ce.start_time, ce.end_time, a.id, a.uuid, crm_provider_id, calendar_event_id, title,
playbook_category_id, user_id, lead_id, contact_id, account_id, opportunity_id,
scheduled_start_time, scheduled_end_time, actual_start_time, actual_end_time, a.created_at
FROM activities a
join calendar_events ce on a.calendar_event_id = ce.id
WHERE a.id IN (608818, 608821);
select * from users where team_id = 1;
select * from team_settings where team_id = 1;
select * from crm_profiles where crm_configuration_id = 39 order by user_id;
select * from team_features where team_id = 1;
select * from users where team_id = 2;
SELECT * FROM activities WHERE uuid_to_bin('ec7647e9-5225-458b-b475-f31aa2769204') = uuid; # 612639
# Preslava N. Ivanova, grou id 3
SELECT * FROM opportunities WHERE uuid_to_bin('a2928fe5-aec5-46cb-85d9-7654c89e46a6') = uuid;
select * from activities where opportunity_id = 344 and actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00';
select
a.id,
a.type,
a.scheduled_start_time,
a.actual_start_time,
a.created_at,
a.opportunity_id,
a.status
FROM activities a
WHERE opportunity_id = 344
and status IN ('completed', 'received', 'delivered')
and (
(a.actual_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')
OR (a.created_at between '2024-10-11 00:00:00' and '2024-10-12 00:00:00')
OR (a.scheduled_start_time between '2024-10-11 00:00:00' and '2024-10-12 00:00:00'))
;
SELECT * FROM users WHERE id = 222;
SELECT * FROM crm_profiles WHERE user_id = 222;
select * from crm_layouts where crm_configuration_id = 39;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 281;
select * from group_deal_risk_types;
select * from opportunities where team_id = 1;
SELECT * FROM opportunities WHERE id = 315;
SELECT * FROM crm_field_data WHERE object_id = 315;
select * from crm_field_data where object_id = 260;
select * from generic_ai_prompts where subject_id = 315;
select * from teams; # 36, 21, 121, [EMAIL]
SELECT * FROM social_accounts WHERE sociable_id = 121 and provider = 'bullhorn';
# [PASSWORD_DOTS]
select * from teams where id = 1;
select * from crm_configurations where id = 39;
select * from users where team_id = 1;
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 1;
# 1 - 00541000004281rAAA
# 204 - 0052g000003freeAAA
# 429 - 0052g000003qGOiAAM
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce';
select * from activities where type = 'softphone'
and created_at > '2024-12-11 15:24:36' order by id desc;
select * from activity_providers where team_id = 1;
select * from activity_provider_users where activity_provider_id = 328;
select * from opportunities where crm_configuration_id = 39
AND account_id = 178 AND is_closed = false
order by created_at DESC;
select * from contacts where id = 3952;
select * from accounts where id = 178;
# [PASSWORD_DOTS]
select * from teams where id = 36;
select * from crm_configurations where id = 21;
select * from users where team_id = 36;
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 36;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 36
and sa.provider = 'bullhorn';
select * from social_accounts where id = 348;
UPDATE social_accounts SET
provider_user_token = '21442_6802599_91:41179a58-21e7-4d7c-ad58-56bb666b2f65',
provider_refresh_token = '21442_6802599_91:01c6b335-3f2a-42e4-85ff-8a08fa65fceb',
expires = 1733998131,
state = 'connected'
WHERE id = 348;
# [PASSWORD_DOTS]
select * from teams where id = 31;
select * from crm_configurations where id = 18;
select * from users where team_id = 31; # 257
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 31;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 31
and sa.provider = 'close';
select * from contacts where crm_configuration_id = 18;
# [PASSWORD_DOTS] NEPTUNE [PASSWORD_DOTS]
select * from teams;
select * from users where id IN (1030, 1035, 1052);
select * from crm_configurations;
select * from users where team_id = 65; # 257
select * from team_settings where team_id = 65; # 257
select * from invitations where team_id = 65; # 257
select * from users where email = '[EMAIL]'; # 257
select u.email, cp.* from users u
join crm_profiles cp on u.id = cp.user_id
where u.team_id = 65;
select * from crm_configurations where id = 53;
select * from accounts where crm_configuration_id = 53 order by id desc;
select * from leads where crm_configuration_id = 53 order by id desc;
select * from contacts where crm_configuration_id = 53 order by id desc;
select * from opportunities where crm_configuration_id = 53 order by id desc;
select * from crm_profiles where crm_configuration_id = 53 order by id desc;
select * from crm_fields where crm_configuration_id = 53 order by id desc;
select * from crm_field_values where crm_field_id = 3341 order by id desc;
select * from crm_layouts where crm_configuration_id = 53 order by id desc;
select * from stages where crm_configuration_id = 53 order by id desc;
select * from crm_profiles where crm_configuration_id = 13;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 65
and sa.provider = 'integration-app';
select * from contacts where crm_configuration_id = 13;
select * from social_accounts where sociable_id = 283;
SELECT * FROM opportunities WHERE crm_provider_id = '006O400000E9bzeIAB';
select * from activity_providers where team_id = 65;
SELECT * FROM activities WHERE crm_configuration_id IN (51, 52, 53);
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 65
;
# [PASSWORD_DOTS] STAGING [PASSWORD_DOTS]
SELECT * FROM teams;
SELECT * FROM teams WHERE id = 88;
SELECT * FROM teams WHERE id = 89;
select * from team_settings where team_id = 89;
SELECT * FROM users WHERE team_id = 89;
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 89;
select * from users;
SELECT * FROM social_accounts WHERE sociable_id = 1761;
SELECT * FROM crm_configurations WHERE id = 70;
select * from accounts where crm_configuration_id = 70 order by id desc;
select * from leads where crm_configuration_id = 70 order by id desc;
select * from contacts where crm_configuration_id = 70 order by id desc;
select * from opportunities where crm_configuration_id = 70 order by id desc;
select * from crm_profiles where crm_configuration_id = 70 order by id desc;
select * from crm_fields where crm_configuration_id = 70 order by id desc;
select * from crm_field_values where crm_field_id = 3536 order by id desc;
select * from crm_layouts where crm_configuration_id = 70 order by id desc;
select * from stages where crm_configuration_id = 70 order by id desc;
select * from business_processes where crm_configuration_id = 70 order by id desc;
select * from business_process_stages where business_process_id = 34;
select * from contacts where id = 10468;
select * from crm_layouts where crm_configuration_id = 70;
SELECT * FROM crm_layout_entities WHERE crm_layout_id = 388;
SELECT * FROM crm_fields WHERE id IN (3533,3534,3535);
select * from activities where crm_configuration_id = 70
and (account_id IS NOT NULL or lead_id IS NOT NULL or contact_id IS NOT NULL or opportunity_id IS NOT NULL) order by id desc;
SELECT * FROM activities WHERE uuid_to_bin('2e10b60f-8a61-41c5-a3d4-28835353dc65') = uuid;
SELECT * FROM activities where crm_configuration_id = 69 ;
SELECT * FROM users WHERE email LIKE '%[EMAIL]%';
SELECT * FROM activities WHERE uuid_to_bin('5a150c93-40fc-42ec-b3bd-c1d328e09f6e') = uuid;
SELECT * FROM opportunities WHERE id = 385;
select * from participants p
join activities a on p.activity_id = a.id
where a.crm_configuration_id = 70
and (p.lead_id IS NOT NULL or p.contact_id IS NOT NULL);
SELECT * FROM participants WHERE id = 1013638;
select * from teams where id = 90;
select * from users where team_id = 90;
select * from social_accounts where social_accounts.sociable_id IN (1960,1760);
SELECT * FROM crm_profiles WHERE crm_configuration_id = 71;
select * from invitations where team_id = 90;
select * from crm_configurations where id = 71;
select * from accounts where crm_configuration_id = 71 order by id desc;
select * from leads where crm_configuration_id = 71 order by id desc;
select * from contacts where crm_configuration_id = 71 order by id desc;
select * from opportunities where crm_configuration_id = 71 order by id desc;
select * from crm_profiles where crm_configuration_id = 71 order by id desc;
select * from crm_fields where crm_configuration_id = 71 order by id desc;
select * from crm_field_values where crm_field_id = 3341 order by id desc;
select * from crm_layouts where crm_configuration_id = 71 order by id desc;
select * from stages where crm_configuration_id = 71 order by id desc;
select * from users order by secondary_email desc;
select u.id, u.email, u.status, sa.id, sa.provider_user_id from social_accounts sa
join users u on sa.sociable_id = u.id
where sa.provider = 'google' and u.email LIKE 'aneliya%';
select * from failed_jobs order by id desc;
select * from users where email = '[EMAIL]' or secondary_email = '[EMAIL]';
select * from teams;
SELECT * FROM crm_profiles WHERE crm_configuration_id = 39;
SELECT * FROM crm_fields WHERE crm_configuration_id = 39 and object_type = 'task';
select * from social_accounts sa
join users u on sa.sociable_id = u.id
where u.team_id = 1
and sa.provider = 'salesforce';
# [PASSWORD_DOTS]
SELECT * FROM activities WHERE uuid_to_bin('c38b3895-fd0f-4b1f-9fb2-c170dba137c6') = uuid;
SELECT * FROM crm_configurations WHERE id = 70;
select * from teams where id = 1;
select * from groups where team_id = 1;
select * from users where team_id = 1;
select o.id, o.name,o.close_date, u.id, u.name, u.group_id, r.id, r.display_name, g.name, g.scope from opportunities o
join users u on o.user_id = u.id
join groups g on u.group_id = g.id
join...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53088
|
1861
|
3
|
2026-05-18T11:35:05.262828+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104105262_m2.jpg...
|
PhpStorm
|
faVsco.js – CoreUserRequest.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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}]...
|
8243381250999052583
|
-8204424741936591934
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
PhostormVIewINavicarecodeLaravelRefactorTOOISWindowmelpFV faVsco.js°9 master k >Project® ActivityController.php© BaseService.php© SoftPhoneManager.php› D ConferenceHandlerv D ConferenceManager© CoreUserRequest.php xTextmessagingservice.orgclass CoreUserRequestextends Request© sortPnonemanager.onp© ConferenceCallbackHandler.php© ConferenceManager.phpDDIO› DEvent>D Exceptionprivate sortmaryrnonenumberpravace ssecondaryrnonenumberw Job>W ResolvelService>DVOprivace sclcle.pravace array sroles.© TwilioConstants.phpc)TwiioReoositorv.onoUoloadenprivate array SrawRequest.7 UrlGeneratorQUtilityM UuidO WaveformM WehhookeD Workflow> D ConfigurationConsole~ D Commands› D Activities> D Analytics>D Calendarsv 0Crm>D Hubspot› IntegrationApp59 0>public function createFromRequestPayload($request): self{...}123public function getNameOf...@return mixed135public function getUserNameOf….}1 editv Accept File *- X Reject File +*€Local ChangesShelfLog x• Chandes o tlles= env.local apo© CoreUserRequest.php app/DTO/SCIM/AAD/Request(C)JiminnvDebuaCommand.oho apo/Console/Commandsphp logging.php config@ SoftPhoneManager.pho app/Component/Twilio/Conference/ConferenceManadC) TeytMessacinaService.oho ann/Services/Telenhonv→ E Side-by-side viewer@cb4ebf0c config/logging.phpDo not ignore'driver' =>'Level' => env('LOG_LEVEL', 'info'),'path' => storage_path('Logs/Laravel.Log'),v Unversioned Siles O filed=.env.nikilocal appenv.other ann© CanAccessAiReportsTest.php tests/Unit/Policies(C) CreateMockAsk.liminnvRenortResultCommand.nhnann/Console/Commands/RE favicon.ico public= ids.txt appT raw_sql.query.sql app© SimulateWebhooksCommand.php app/Console/Commands/Crm/HubspotM. WEBHOOK_FILTERING_IMPLEMENTATION.md appaco accociated with hranch 'macter' hac heon rectored Il Pollback II Confiaure (15 minutes aao)ACcept RenectE custom.logA console [STAGING] XE laravel.log4 SF [jiminny@localhost]A HS_Jocal [jiminny@localhost]A console [PROD]# console [euy556562577Tx: AutovTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE WD:S. jiminny_jupiterBISBIS XIA Vselect * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team.id = t.idWHERE r.fcequency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expices.at >= now() OR r.expices.at IS NULL);select * from automated_report_results where ceportaid IN (18, 33);select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;select * from activity_searches where id = 10932;select * from activity_search_filters where activity_search_id = 10932;select * tromorder by 10 descresults order by id desc;automated reports where 10 IN (55)select * from automated_report_results where ig IN (81);select * trom users where 1d IN 00055,15787,11985011select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:• suppont Dally • In zom100% 2Mon 18 May 14:35:05U AskJiminnyReportActivityServiceTest~CascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..sedrcned class. Kole in ap.inougntror is1Analyze current selm implementation and role permission systemReview validation rules for roles and permissionsiDesign SCIM role assignment schemaImolement role mutator for SCIMAdd validation to SCIM role assianmentWrite unit tests for SCIM role managementUodate SCIM documentationkead scimProvisioning.onp and s otnertlles >Thought for 1s›Thought for 2s>Searched ROLE_RECORDERIROLE_VOICEIROLE_ANALYST|ROLE_LISTENER in app/Models/User.phpThought for 2sSearched PERMISSIONADMINPERMISSIONMANAGER In app/ModelsRead RolesAndCrmOntionMismatchRule.ohdAsk anything (&AL)+ •code SWE-1.6Current versionReject allAccept all1ditterenceМАAAAAY"driver' = 'errorlog','Level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel. 10g'),'custom_channel' »> ['driver' →> 'single','path' => storage_path('logs/custom.log'),'Level' => env('LOG_LEVEL', 'info'),W Windsurf Toams 54-1UTF.8f 4 spaces...
|
53087
|
NULL
|
NULL
|
NULL
|
|
53089
|
1860
|
3
|
2026-05-18T11:35:24.210573+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104124210_m1.jpg...
|
PhpStorm
|
faVsco.js – Constants.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴2DOCKER (docker-compose)asticsearch", "data"],"pid":7,"message": "No livingconnections"}kibana1 {"type" : "log", "@timestamp":"2026-05-18T11:35:18Z", "tags" : ["error"ins","plug, "taskManager""taskManager"], "pid" :7, "message" : "Failed to pollfor work: Error: NoLiving connections"}kibana1 {"type": "log","@timestamp": "2026-05-18T11:35:19Z""tags" : ["warning"elasticsearch", "data"], "pid" :7,"message" : "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:35: 197", "tags" : ["warning", "elasticsearch","data"], "pid" :7, "message" : "No livingconnections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:35:19Z", "tags" : ["warning"ugins""licensing"],"pid" :7, "message" : "License informationcould not be obtained from Elasticsearch due to Error: No Living connections error"}kibana1 {"type" : "log","@timestamp" : "2026-05-18T11:35:19Z", "tags" : ["warning", "elasticsearch", "monitoring"], "pid":7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:35:19Z","tags" : ["warning", "elasticsearch", "monitoring"],"pid" :7,"message":"No living connections"}kibana1 {"type":"log" ,"@timestamp":"2026-05-18T11:35:19Z"."tags" : ["warning","plugins","licensing"], "pid" :7, "message" : "License information could not be obtained from Elasticsearch dueto Error: No Livingconnectionskibana1 {"type" : "log""@timestamp" : "2026-05-18T11:35:21Z","tags" : ["error"ticsearch"arch, "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}kibana1 {"type" : "log""@timestamp" : "2026-05-18T11:35 :21Z","tags" : ["error", "elasticsearch","data"],"pid":7,'"message":"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch: 9200"}kibana1 {"type" : "log","@timestamp" : "2026-05-18T11:35:21Z" , "tags" : ["warning", "elasticsearch", "data"],"pid":7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:35:21Z", "tags" : ["warning", "elasticsearch", "data"], "pid":7, "message": "No livingconnections"}kibana1 {"type" : "log".ins",,"@timestamp" : "2026-05-18T11:35:21Z","tags" : ["error", "plug, "taskManager", "taskManager"], "pid" :7, "message" : "Failed to poll for work: Error: NoLivingconnections"}docker_lamp_112026-05-18 11:35:15 Running ['artisan' dialers:monitor-activities].7S DONEdocker_lamp_11 • '/usr/local/bin/php' 'artisan' dialers:monitor-activities › '/proc/1/fd/1' 2>&1kibana1 {"type": "log", "@timestamp": "2026-05-18T11:35:23Z", "tags" : ["error", "elasticsearch", "data"],"pid" :7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}View in Docker Desktop• View ConfigEnable WatchHomeDMsActivityFilesLaterMore→Jiminny ...Metrorrtence# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...• Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov ". Mario Georgiev. Nikolay IvanovLo James Graham *8 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiE. Lukas Kovalik y...AppsJira CloudToastSupport Daily • in 25 ml100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas@ Files• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:35:246 0+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
8200747228751208891
|
NULL
|
idle
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴2DOCKER (docker-compose)asticsearch", "data"],"pid":7,"message": "No livingconnections"}kibana1 {"type" : "log", "@timestamp":"2026-05-18T11:35:18Z", "tags" : ["error"ins","plug, "taskManager""taskManager"], "pid" :7, "message" : "Failed to pollfor work: Error: NoLiving connections"}kibana1 {"type": "log","@timestamp": "2026-05-18T11:35:19Z""tags" : ["warning"elasticsearch", "data"], "pid" :7,"message" : "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:35: 197", "tags" : ["warning", "elasticsearch","data"], "pid" :7, "message" : "No livingconnections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:35:19Z", "tags" : ["warning"ugins""licensing"],"pid" :7, "message" : "License informationcould not be obtained from Elasticsearch due to Error: No Living connections error"}kibana1 {"type" : "log","@timestamp" : "2026-05-18T11:35:19Z", "tags" : ["warning", "elasticsearch", "monitoring"], "pid":7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:35:19Z","tags" : ["warning", "elasticsearch", "monitoring"],"pid" :7,"message":"No living connections"}kibana1 {"type":"log" ,"@timestamp":"2026-05-18T11:35:19Z"."tags" : ["warning","plugins","licensing"], "pid" :7, "message" : "License information could not be obtained from Elasticsearch dueto Error: No Livingconnectionskibana1 {"type" : "log""@timestamp" : "2026-05-18T11:35:21Z","tags" : ["error"ticsearch"arch, "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}kibana1 {"type" : "log""@timestamp" : "2026-05-18T11:35 :21Z","tags" : ["error", "elasticsearch","data"],"pid":7,'"message":"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch: 9200"}kibana1 {"type" : "log","@timestamp" : "2026-05-18T11:35:21Z" , "tags" : ["warning", "elasticsearch", "data"],"pid":7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:35:21Z", "tags" : ["warning", "elasticsearch", "data"], "pid":7, "message": "No livingconnections"}kibana1 {"type" : "log".ins",,"@timestamp" : "2026-05-18T11:35:21Z","tags" : ["error", "plug, "taskManager", "taskManager"], "pid" :7, "message" : "Failed to poll for work: Error: NoLivingconnections"}docker_lamp_112026-05-18 11:35:15 Running ['artisan' dialers:monitor-activities].7S DONEdocker_lamp_11 • '/usr/local/bin/php' 'artisan' dialers:monitor-activities › '/proc/1/fd/1' 2>&1kibana1 {"type": "log", "@timestamp": "2026-05-18T11:35:23Z", "tags" : ["error", "elasticsearch", "data"],"pid" :7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}View in Docker Desktop• View ConfigEnable WatchHomeDMsActivityFilesLaterMore→Jiminny ...Metrorrtence# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...• Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov ". Mario Georgiev. Nikolay IvanovLo James Graham *8 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiE. Lukas Kovalik y...AppsJira CloudToastSupport Daily • in 25 ml100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas@ Files• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:35:246 0+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53090
|
1861
|
4
|
2026-05-18T11:35:36.955521+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104136955_m2.jpg...
|
PhpStorm
|
faVsco.js – Constants.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
namespace Jiminny\Component\SCIM;
use Jiminny\Component\SCIM\Builders\Filters;
class Constants
{
/**
* HTTP codes
*/
public const HTTP_OK = 200;
public const HTTP_CREATED = 201;
public const HTTP_NO_CONTENT = 204;
public const HTTP_NOT_FOUND = 404;
public const HTTP_BAD_REQUEST = 400;
public const HTTP_ACCESS_DENIED = 403;
public const HTTP_ERROR = 501;
/**
* Users resource constants
*/
public const USER_FIRST_NAME = 'givenName';
public const USER_FAMILY_NAME = 'familyName';
public const USER_STATUS = 'Active';
public const USER_USERNAME = 'UserName';
public const USER_EXTERNAL_ID = 'externalId';
public const USER_FIELDS = [
self::USER_FIRST_NAME,
self::USER_FAMILY_NAME,
self::USER_STATUS,
self::USER_USERNAME,
];
public const META_OPERATIONS_REPLACE = 'replace';
public const META_OPERATIONS_REPLACE_UPPER = 'Replace';
public const META_OPERATIONS_ADD = 'add';
public const META_OPERATIONS_ADD_UPPER = 'Add';
public const META_OPERATIONS_REMOVE_UPPER = 'Remove';
public const META_OPERATIONS_REMOVE = 'remove';
public const META_ID = 'id';
public const META_PHONE_NUMBERS = 'phoneNumbers';
public const META_TITLE = 'title';
public const META_ROLES = 'roles';
public const META_PATH = 'path';
public const META_ENTERPRISE_SCHEMA = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User';
public const META_DEPARTMENT = 'department';
public const META_META = 'meta';
public const META_TYPE = 'type';
public const META_VALUE = 'value';
public const META_OPERATIONS = 'Operations';
public const META_OP = 'op';
public const META_RESOURCE_TYPE = 'resourceType';
public const META_CREATED = 'created';
public const META_LAST_MODIFIED = 'lastModified';
public const META_FORMATTED = 'formatted';
public const META_EMAILS = 'emails';
public const META_USERNAME = 'userName';
public const META_NAME = 'name';
public const META_STATUS = 'active';
public const META_PRIMARY = 'primary';
public const META_ERROR_STATUS = 'status';
public const META_ERROR_DETAIL = 'detail';
public const META_LIST_TOTAL_RESULTS = 'totalResults';
public const META_LIST_RESOURCES = 'Resources';
public const META_LIST_START_INDEX = 'startIndex';
public const META_LIST_COUNT = 'count';
public const META_LIST_ITEMS_PER_PAGE = 'itemsPerPage';
public const META_SCHEMAS = 'schemas';
public const META_FILTER = 'filter';
public const META_TYPE_WORK = 'work';
public const META_PHONE_NUMBER_TYPE_MOBILE = 'mobile';
public const META_EMAIL_TYPE_HOME = 'home';
public const META_CONTAINER_NAME = 'name';
public const META_CONTAINER_EMAILS = 'emails';
public const RESOURCE_TYPE_USER = 'User';
public const USER_PATCH_ATTR_TITLE = 'title';
public const USER_PATCH_ATTR_ACTIVE = 'active';
public const USER_PATCH_ATTR_USERNAME = 'userName';
public const USER_PATCH_ATTR_FIRST_NAME = 'name.givenName';
public const USER_PATCH_ATTR_LAST_NAME = 'name.familyName';
public const USER_PATCH_ATTR_PRIMARY_EMAIL = 'emails[type eq "work"].value';
public const USER_PATCH_ATTR_SECONDARY_EMAIL = 'emails[type eq "home"].value';
public const USER_PATCH_ATTR_PHONE_NUMBER = 'phoneNumbers[type eq "work"].value';
public const USER_PATCH_ATTR_PHONE_NUMBER_MOBILE = 'phoneNumbers[type eq "mobile"].value';
public const USER_PATCH_ATTR_ROLES = 'roles';
public const USER_SUPPORTED_PATCH_ATTRIBUTES_MUTATORS = [
self::USER_PATCH_ATTR_TITLE => Mutators\Attributes\User\TitleAttr::class,
self::USER_PATCH_ATTR_ACTIVE => Mutators\Attributes\User\ActiveAttr::class,
self::USER_PATCH_ATTR_USERNAME => Mutators\Attributes\User\UsernameAttr::class,
self::USER_PATCH_ATTR_FIRST_NAME => Mutators\Attributes\User\FirstNameAttr::class,
self::USER_PATCH_ATTR_LAST_NAME => Mutators\Attributes\User\LastNameAttr::class,
self::USER_PATCH_ATTR_PRIMARY_EMAIL => Mutators\Attributes\User\PrimaryEmailAttr::class,
self::USER_PATCH_ATTR_SECONDARY_EMAIL => Mutators\Attributes\User\SecondaryEmailAttr::class,
self::USER_PATCH_ATTR_PHONE_NUMBER => Mutators\Attributes\User\PhoneNumberPrimaryAttr::class,
self::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE => Mutators\Attributes\User\PhoneNumberSecondaryAttr::class,
];
public const FILTER_OPERATOR_EQ = 'eq';
public const SUPPORTED_FILTERS_OPERATORS = [
self::FILTER_OPERATOR_EQ,
];
public const USER_SUPPORTED_FILTER_ATTRIBUTES_QUERY_BUILDERS = [
self::USER_FILTER_LOCAL_ID => Filters\User\LocalIdFilter::class,
self::USER_FILTER_USERNAME => Filters\User\UserNameFilter::class,
self::USER_FILTER_FIRST_NAME => Filters\User\FirstNameFilter::class,
self::USER_FILTER_LAST_NAME => Filters\User\LastNameFilter::class,
self::USER_FILTER_EXTERNAL_ID => Filters\User\ExternalIdFilter::class,
self::USER_FILTER_DISPLAY_NAME => Filters\User\DisplayNameFilter::class,
];
public const USER_FILTER_LOCAL_ID = 'id';
public const USER_FILTER_USERNAME = 'userName';
public const USER_FILTER_FIRST_NAME = 'givenName';
public const USER_FILTER_LAST_NAME = 'familyName';
public const USER_FILTER_EXTERNAL_ID = 'externalId';
public const USER_FILTER_DISPLAY_NAME = 'DisplayName';
public const SCIM_SUPPORTED_RESOURCES = [
self::USER_RESOURCE_PREFIX,
self::GROUP_RESOURCE_PREFIX,
];
public const USER_RESOURCE_PREFIX = 'Users';
public const GROUP_RESOURCE_PREFIX = 'Groups';
public const OUTGOING_SCHEMAS_SUPPORTED = [
self::OUTGOING_CORE_USER,
];
/**
* Outgoing supported schemas
*/
public const OUTGOING_CORE_USER = 'urn:ietf:params:scim:schemas:core:2.0:User';
public const OUTGOING_ERROR = 'urn:ietf:params:scim:api:messages:2.0:Error';
public const OUTGOING_LIST_RESPONSE = 'urn:ietf:params:scim:api:messages:2.0:ListResponse';
public const OUTGOING_CORE_GROUP = 'urn:ietf:params:scim:schemas:core:2.0:Group';
/**
* Groups resource constants
*/
public const GROUP_ATTRIBUTE_DISPLAY_NAME = 'displayName';
public const GROUP_ATTRIBUTE_EXTERNAL_ID = 'externalId';
public const GROUP_ATTRIBUTE_ID = 'id';
public const RESOURCE_TYPE_GROUP = 'Group';
public const GROUP_FILTER_QUERY_NAME = 'filter';
public const GROUP_EXCLUDED_ATTRIBUTES = 'excludedAttributes';
public const GROUP_SUPPORTED_FILTER_ATTRIBUTES = [
self::GROUP_ATTRIBUTE_DISPLAY_NAME,
];
public const GROUP_MEMBERS = 'members';
public const SUPPORTED_PATCH_OPERATIONS = [
self::META_OPERATIONS_ADD,
self::META_OPERATIONS_REPLACE,
self::META_OPERATIONS_REMOVE,
];
public const GROUP_PATCH_ATTRIBUTE_MEMBERS = 'members';
public const GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME = 'displayName';
public const GROUP_SUPPORTED_PATCH_ATTRIBUTES_MUTATORS = [
self::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME => Mutators\Attributes\Group\DisplayNameAttr::class,
self::GROUP_PATCH_ATTRIBUTE_MEMBERS => Mutators\Attributes\Group\MembersAttr::class,
];
public const GROUP_FILTER_DISPLAY_NAME = 'displayname';
public const GROUP_SUPPORTED_FILTER_ATTRIBUTES_QUERY_BUILDERS = [
self::GROUP_FILTER_DISPLAY_NAME => Filters\Group\DisplayNameFilter::class,
];
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results...
|
[{"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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Component\\SCIM;\n\nuse Jiminny\\Component\\SCIM\\Builders\\Filters;\n\nclass Constants\n{\n /**\n * HTTP codes\n */\n public const HTTP_OK = 200;\n public const HTTP_CREATED = 201;\n public const HTTP_NO_CONTENT = 204;\n\n public const HTTP_NOT_FOUND = 404;\n public const HTTP_BAD_REQUEST = 400;\n public const HTTP_ACCESS_DENIED = 403;\n\n public const HTTP_ERROR = 501;\n\n /**\n * Users resource constants\n */\n public const USER_FIRST_NAME = 'givenName';\n public const USER_FAMILY_NAME = 'familyName';\n public const USER_STATUS = 'Active';\n public const USER_USERNAME = 'UserName';\n public const USER_EXTERNAL_ID = 'externalId';\n\n public const USER_FIELDS = [\n self::USER_FIRST_NAME,\n self::USER_FAMILY_NAME,\n self::USER_STATUS,\n self::USER_USERNAME,\n ];\n\n public const META_OPERATIONS_REPLACE = 'replace';\n public const META_OPERATIONS_REPLACE_UPPER = 'Replace';\n public const META_OPERATIONS_ADD = 'add';\n public const META_OPERATIONS_ADD_UPPER = 'Add';\n public const META_OPERATIONS_REMOVE_UPPER = 'Remove';\n public const META_OPERATIONS_REMOVE = 'remove';\n\n public const META_ID = 'id';\n public const META_PHONE_NUMBERS = 'phoneNumbers';\n public const META_TITLE = 'title';\n public const META_ROLES = 'roles';\n public const META_PATH = 'path';\n public const META_ENTERPRISE_SCHEMA = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User';\n public const META_DEPARTMENT = 'department';\n public const META_META = 'meta';\n public const META_TYPE = 'type';\n public const META_VALUE = 'value';\n public const META_OPERATIONS = 'Operations';\n public const META_OP = 'op';\n public const META_RESOURCE_TYPE = 'resourceType';\n public const META_CREATED = 'created';\n public const META_LAST_MODIFIED = 'lastModified';\n public const META_FORMATTED = 'formatted';\n public const META_EMAILS = 'emails';\n public const META_USERNAME = 'userName';\n public const META_NAME = 'name';\n public const META_STATUS = 'active';\n public const META_PRIMARY = 'primary';\n public const META_ERROR_STATUS = 'status';\n public const META_ERROR_DETAIL = 'detail';\n public const META_LIST_TOTAL_RESULTS = 'totalResults';\n public const META_LIST_RESOURCES = 'Resources';\n public const META_LIST_START_INDEX = 'startIndex';\n public const META_LIST_COUNT = 'count';\n public const META_LIST_ITEMS_PER_PAGE = 'itemsPerPage';\n public const META_SCHEMAS = 'schemas';\n public const META_FILTER = 'filter';\n public const META_TYPE_WORK = 'work';\n public const META_PHONE_NUMBER_TYPE_MOBILE = 'mobile';\n public const META_EMAIL_TYPE_HOME = 'home';\n public const META_CONTAINER_NAME = 'name';\n public const META_CONTAINER_EMAILS = 'emails';\n\n public const RESOURCE_TYPE_USER = 'User';\n\n public const USER_PATCH_ATTR_TITLE = 'title';\n public const USER_PATCH_ATTR_ACTIVE = 'active';\n public const USER_PATCH_ATTR_USERNAME = 'userName';\n public const USER_PATCH_ATTR_FIRST_NAME = 'name.givenName';\n public const USER_PATCH_ATTR_LAST_NAME = 'name.familyName';\n public const USER_PATCH_ATTR_PRIMARY_EMAIL = 'emails[type eq \"work\"].value';\n public const USER_PATCH_ATTR_SECONDARY_EMAIL = 'emails[type eq \"home\"].value';\n public const USER_PATCH_ATTR_PHONE_NUMBER = 'phoneNumbers[type eq \"work\"].value';\n public const USER_PATCH_ATTR_PHONE_NUMBER_MOBILE = 'phoneNumbers[type eq \"mobile\"].value';\n public const USER_PATCH_ATTR_ROLES = 'roles';\n\n public const USER_SUPPORTED_PATCH_ATTRIBUTES_MUTATORS = [\n self::USER_PATCH_ATTR_TITLE => Mutators\\Attributes\\User\\TitleAttr::class,\n self::USER_PATCH_ATTR_ACTIVE => Mutators\\Attributes\\User\\ActiveAttr::class,\n self::USER_PATCH_ATTR_USERNAME => Mutators\\Attributes\\User\\UsernameAttr::class,\n self::USER_PATCH_ATTR_FIRST_NAME => Mutators\\Attributes\\User\\FirstNameAttr::class,\n self::USER_PATCH_ATTR_LAST_NAME => Mutators\\Attributes\\User\\LastNameAttr::class,\n self::USER_PATCH_ATTR_PRIMARY_EMAIL => Mutators\\Attributes\\User\\PrimaryEmailAttr::class,\n self::USER_PATCH_ATTR_SECONDARY_EMAIL => Mutators\\Attributes\\User\\SecondaryEmailAttr::class,\n self::USER_PATCH_ATTR_PHONE_NUMBER => Mutators\\Attributes\\User\\PhoneNumberPrimaryAttr::class,\n self::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE => Mutators\\Attributes\\User\\PhoneNumberSecondaryAttr::class,\n ];\n\n public const FILTER_OPERATOR_EQ = 'eq';\n\n public const SUPPORTED_FILTERS_OPERATORS = [\n self::FILTER_OPERATOR_EQ,\n ];\n\n public const USER_SUPPORTED_FILTER_ATTRIBUTES_QUERY_BUILDERS = [\n self::USER_FILTER_LOCAL_ID => Filters\\User\\LocalIdFilter::class,\n self::USER_FILTER_USERNAME => Filters\\User\\UserNameFilter::class,\n self::USER_FILTER_FIRST_NAME => Filters\\User\\FirstNameFilter::class,\n self::USER_FILTER_LAST_NAME => Filters\\User\\LastNameFilter::class,\n self::USER_FILTER_EXTERNAL_ID => Filters\\User\\ExternalIdFilter::class,\n self::USER_FILTER_DISPLAY_NAME => Filters\\User\\DisplayNameFilter::class,\n ];\n\n public const USER_FILTER_LOCAL_ID = 'id';\n public const USER_FILTER_USERNAME = 'userName';\n public const USER_FILTER_FIRST_NAME = 'givenName';\n public const USER_FILTER_LAST_NAME = 'familyName';\n public const USER_FILTER_EXTERNAL_ID = 'externalId';\n public const USER_FILTER_DISPLAY_NAME = 'DisplayName';\n\n public const SCIM_SUPPORTED_RESOURCES = [\n self::USER_RESOURCE_PREFIX,\n self::GROUP_RESOURCE_PREFIX,\n ];\n\n public const USER_RESOURCE_PREFIX = 'Users';\n public const GROUP_RESOURCE_PREFIX = 'Groups';\n\n public const OUTGOING_SCHEMAS_SUPPORTED = [\n self::OUTGOING_CORE_USER,\n\n ];\n\n /**\n * Outgoing supported schemas\n */\n public const OUTGOING_CORE_USER = 'urn:ietf:params:scim:schemas:core:2.0:User';\n public const OUTGOING_ERROR = 'urn:ietf:params:scim:api:messages:2.0:Error';\n public const OUTGOING_LIST_RESPONSE = 'urn:ietf:params:scim:api:messages:2.0:ListResponse';\n\n public const OUTGOING_CORE_GROUP = 'urn:ietf:params:scim:schemas:core:2.0:Group';\n\n /**\n * Groups resource constants\n */\n public const GROUP_ATTRIBUTE_DISPLAY_NAME = 'displayName';\n public const GROUP_ATTRIBUTE_EXTERNAL_ID = 'externalId';\n public const GROUP_ATTRIBUTE_ID = 'id';\n public const RESOURCE_TYPE_GROUP = 'Group';\n public const GROUP_FILTER_QUERY_NAME = 'filter';\n public const GROUP_EXCLUDED_ATTRIBUTES = 'excludedAttributes';\n\n public const GROUP_SUPPORTED_FILTER_ATTRIBUTES = [\n self::GROUP_ATTRIBUTE_DISPLAY_NAME,\n ];\n\n public const GROUP_MEMBERS = 'members';\n\n public const SUPPORTED_PATCH_OPERATIONS = [\n self::META_OPERATIONS_ADD,\n self::META_OPERATIONS_REPLACE,\n self::META_OPERATIONS_REMOVE,\n ];\n\n public const GROUP_PATCH_ATTRIBUTE_MEMBERS = 'members';\n public const GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME = 'displayName';\n\n public const GROUP_SUPPORTED_PATCH_ATTRIBUTES_MUTATORS = [\n self::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME => Mutators\\Attributes\\Group\\DisplayNameAttr::class,\n self::GROUP_PATCH_ATTRIBUTE_MEMBERS => Mutators\\Attributes\\Group\\MembersAttr::class,\n ];\n\n public const GROUP_FILTER_DISPLAY_NAME = 'displayname';\n\n public const GROUP_SUPPORTED_FILTER_ATTRIBUTES_QUERY_BUILDERS = [\n self::GROUP_FILTER_DISPLAY_NAME => Filters\\Group\\DisplayNameFilter::class,\n ];\n}","depth":4,"on_screen":true,"value":"<?php\n\nnamespace Jiminny\\Component\\SCIM;\n\nuse Jiminny\\Component\\SCIM\\Builders\\Filters;\n\nclass Constants\n{\n /**\n * HTTP codes\n */\n public const HTTP_OK = 200;\n public const HTTP_CREATED = 201;\n public const HTTP_NO_CONTENT = 204;\n\n public const HTTP_NOT_FOUND = 404;\n public const HTTP_BAD_REQUEST = 400;\n public const HTTP_ACCESS_DENIED = 403;\n\n public const HTTP_ERROR = 501;\n\n /**\n * Users resource constants\n */\n public const USER_FIRST_NAME = 'givenName';\n public const USER_FAMILY_NAME = 'familyName';\n public const USER_STATUS = 'Active';\n public const USER_USERNAME = 'UserName';\n public const USER_EXTERNAL_ID = 'externalId';\n\n public const USER_FIELDS = [\n self::USER_FIRST_NAME,\n self::USER_FAMILY_NAME,\n self::USER_STATUS,\n self::USER_USERNAME,\n ];\n\n public const META_OPERATIONS_REPLACE = 'replace';\n public const META_OPERATIONS_REPLACE_UPPER = 'Replace';\n public const META_OPERATIONS_ADD = 'add';\n public const META_OPERATIONS_ADD_UPPER = 'Add';\n public const META_OPERATIONS_REMOVE_UPPER = 'Remove';\n public const META_OPERATIONS_REMOVE = 'remove';\n\n public const META_ID = 'id';\n public const META_PHONE_NUMBERS = 'phoneNumbers';\n public const META_TITLE = 'title';\n public const META_ROLES = 'roles';\n public const META_PATH = 'path';\n public const META_ENTERPRISE_SCHEMA = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User';\n public const META_DEPARTMENT = 'department';\n public const META_META = 'meta';\n public const META_TYPE = 'type';\n public const META_VALUE = 'value';\n public const META_OPERATIONS = 'Operations';\n public const META_OP = 'op';\n public const META_RESOURCE_TYPE = 'resourceType';\n public const META_CREATED = 'created';\n public const META_LAST_MODIFIED = 'lastModified';\n public const META_FORMATTED = 'formatted';\n public const META_EMAILS = 'emails';\n public const META_USERNAME = 'userName';\n public const META_NAME = 'name';\n public const META_STATUS = 'active';\n public const META_PRIMARY = 'primary';\n public const META_ERROR_STATUS = 'status';\n public const META_ERROR_DETAIL = 'detail';\n public const META_LIST_TOTAL_RESULTS = 'totalResults';\n public const META_LIST_RESOURCES = 'Resources';\n public const META_LIST_START_INDEX = 'startIndex';\n public const META_LIST_COUNT = 'count';\n public const META_LIST_ITEMS_PER_PAGE = 'itemsPerPage';\n public const META_SCHEMAS = 'schemas';\n public const META_FILTER = 'filter';\n public const META_TYPE_WORK = 'work';\n public const META_PHONE_NUMBER_TYPE_MOBILE = 'mobile';\n public const META_EMAIL_TYPE_HOME = 'home';\n public const META_CONTAINER_NAME = 'name';\n public const META_CONTAINER_EMAILS = 'emails';\n\n public const RESOURCE_TYPE_USER = 'User';\n\n public const USER_PATCH_ATTR_TITLE = 'title';\n public const USER_PATCH_ATTR_ACTIVE = 'active';\n public const USER_PATCH_ATTR_USERNAME = 'userName';\n public const USER_PATCH_ATTR_FIRST_NAME = 'name.givenName';\n public const USER_PATCH_ATTR_LAST_NAME = 'name.familyName';\n public const USER_PATCH_ATTR_PRIMARY_EMAIL = 'emails[type eq \"work\"].value';\n public const USER_PATCH_ATTR_SECONDARY_EMAIL = 'emails[type eq \"home\"].value';\n public const USER_PATCH_ATTR_PHONE_NUMBER = 'phoneNumbers[type eq \"work\"].value';\n public const USER_PATCH_ATTR_PHONE_NUMBER_MOBILE = 'phoneNumbers[type eq \"mobile\"].value';\n public const USER_PATCH_ATTR_ROLES = 'roles';\n\n public const USER_SUPPORTED_PATCH_ATTRIBUTES_MUTATORS = [\n self::USER_PATCH_ATTR_TITLE => Mutators\\Attributes\\User\\TitleAttr::class,\n self::USER_PATCH_ATTR_ACTIVE => Mutators\\Attributes\\User\\ActiveAttr::class,\n self::USER_PATCH_ATTR_USERNAME => Mutators\\Attributes\\User\\UsernameAttr::class,\n self::USER_PATCH_ATTR_FIRST_NAME => Mutators\\Attributes\\User\\FirstNameAttr::class,\n self::USER_PATCH_ATTR_LAST_NAME => Mutators\\Attributes\\User\\LastNameAttr::class,\n self::USER_PATCH_ATTR_PRIMARY_EMAIL => Mutators\\Attributes\\User\\PrimaryEmailAttr::class,\n self::USER_PATCH_ATTR_SECONDARY_EMAIL => Mutators\\Attributes\\User\\SecondaryEmailAttr::class,\n self::USER_PATCH_ATTR_PHONE_NUMBER => Mutators\\Attributes\\User\\PhoneNumberPrimaryAttr::class,\n self::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE => Mutators\\Attributes\\User\\PhoneNumberSecondaryAttr::class,\n ];\n\n public const FILTER_OPERATOR_EQ = 'eq';\n\n public const SUPPORTED_FILTERS_OPERATORS = [\n self::FILTER_OPERATOR_EQ,\n ];\n\n public const USER_SUPPORTED_FILTER_ATTRIBUTES_QUERY_BUILDERS = [\n self::USER_FILTER_LOCAL_ID => Filters\\User\\LocalIdFilter::class,\n self::USER_FILTER_USERNAME => Filters\\User\\UserNameFilter::class,\n self::USER_FILTER_FIRST_NAME => Filters\\User\\FirstNameFilter::class,\n self::USER_FILTER_LAST_NAME => Filters\\User\\LastNameFilter::class,\n self::USER_FILTER_EXTERNAL_ID => Filters\\User\\ExternalIdFilter::class,\n self::USER_FILTER_DISPLAY_NAME => Filters\\User\\DisplayNameFilter::class,\n ];\n\n public const USER_FILTER_LOCAL_ID = 'id';\n public const USER_FILTER_USERNAME = 'userName';\n public const USER_FILTER_FIRST_NAME = 'givenName';\n public const USER_FILTER_LAST_NAME = 'familyName';\n public const USER_FILTER_EXTERNAL_ID = 'externalId';\n public const USER_FILTER_DISPLAY_NAME = 'DisplayName';\n\n public const SCIM_SUPPORTED_RESOURCES = [\n self::USER_RESOURCE_PREFIX,\n self::GROUP_RESOURCE_PREFIX,\n ];\n\n public const USER_RESOURCE_PREFIX = 'Users';\n public const GROUP_RESOURCE_PREFIX = 'Groups';\n\n public const OUTGOING_SCHEMAS_SUPPORTED = [\n self::OUTGOING_CORE_USER,\n\n ];\n\n /**\n * Outgoing supported schemas\n */\n public const OUTGOING_CORE_USER = 'urn:ietf:params:scim:schemas:core:2.0:User';\n public const OUTGOING_ERROR = 'urn:ietf:params:scim:api:messages:2.0:Error';\n public const OUTGOING_LIST_RESPONSE = 'urn:ietf:params:scim:api:messages:2.0:ListResponse';\n\n public const OUTGOING_CORE_GROUP = 'urn:ietf:params:scim:schemas:core:2.0:Group';\n\n /**\n * Groups resource constants\n */\n public const GROUP_ATTRIBUTE_DISPLAY_NAME = 'displayName';\n public const GROUP_ATTRIBUTE_EXTERNAL_ID = 'externalId';\n public const GROUP_ATTRIBUTE_ID = 'id';\n public const RESOURCE_TYPE_GROUP = 'Group';\n public const GROUP_FILTER_QUERY_NAME = 'filter';\n public const GROUP_EXCLUDED_ATTRIBUTES = 'excludedAttributes';\n\n public const GROUP_SUPPORTED_FILTER_ATTRIBUTES = [\n self::GROUP_ATTRIBUTE_DISPLAY_NAME,\n ];\n\n public const GROUP_MEMBERS = 'members';\n\n public const SUPPORTED_PATCH_OPERATIONS = [\n self::META_OPERATIONS_ADD,\n self::META_OPERATIONS_REPLACE,\n self::META_OPERATIONS_REMOVE,\n ];\n\n public const GROUP_PATCH_ATTRIBUTE_MEMBERS = 'members';\n public const GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME = 'displayName';\n\n public const GROUP_SUPPORTED_PATCH_ATTRIBUTES_MUTATORS = [\n self::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME => Mutators\\Attributes\\Group\\DisplayNameAttr::class,\n self::GROUP_PATCH_ATTRIBUTE_MEMBERS => Mutators\\Attributes\\Group\\MembersAttr::class,\n ];\n\n public const GROUP_FILTER_DISPLAY_NAME = 'displayname';\n\n public const GROUP_SUPPORTED_FILTER_ATTRIBUTES_QUERY_BUILDERS = [\n self::GROUP_FILTER_DISPLAY_NAME => Filters\\Group\\DisplayNameFilter::class,\n ];\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.42785904,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.43650267,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.4474734,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.45611703,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.46476063,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.47573137,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8112917949482775138
|
-4595477849910474289
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
namespace Jiminny\Component\SCIM;
use Jiminny\Component\SCIM\Builders\Filters;
class Constants
{
/**
* HTTP codes
*/
public const HTTP_OK = 200;
public const HTTP_CREATED = 201;
public const HTTP_NO_CONTENT = 204;
public const HTTP_NOT_FOUND = 404;
public const HTTP_BAD_REQUEST = 400;
public const HTTP_ACCESS_DENIED = 403;
public const HTTP_ERROR = 501;
/**
* Users resource constants
*/
public const USER_FIRST_NAME = 'givenName';
public const USER_FAMILY_NAME = 'familyName';
public const USER_STATUS = 'Active';
public const USER_USERNAME = 'UserName';
public const USER_EXTERNAL_ID = 'externalId';
public const USER_FIELDS = [
self::USER_FIRST_NAME,
self::USER_FAMILY_NAME,
self::USER_STATUS,
self::USER_USERNAME,
];
public const META_OPERATIONS_REPLACE = 'replace';
public const META_OPERATIONS_REPLACE_UPPER = 'Replace';
public const META_OPERATIONS_ADD = 'add';
public const META_OPERATIONS_ADD_UPPER = 'Add';
public const META_OPERATIONS_REMOVE_UPPER = 'Remove';
public const META_OPERATIONS_REMOVE = 'remove';
public const META_ID = 'id';
public const META_PHONE_NUMBERS = 'phoneNumbers';
public const META_TITLE = 'title';
public const META_ROLES = 'roles';
public const META_PATH = 'path';
public const META_ENTERPRISE_SCHEMA = 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User';
public const META_DEPARTMENT = 'department';
public const META_META = 'meta';
public const META_TYPE = 'type';
public const META_VALUE = 'value';
public const META_OPERATIONS = 'Operations';
public const META_OP = 'op';
public const META_RESOURCE_TYPE = 'resourceType';
public const META_CREATED = 'created';
public const META_LAST_MODIFIED = 'lastModified';
public const META_FORMATTED = 'formatted';
public const META_EMAILS = 'emails';
public const META_USERNAME = 'userName';
public const META_NAME = 'name';
public const META_STATUS = 'active';
public const META_PRIMARY = 'primary';
public const META_ERROR_STATUS = 'status';
public const META_ERROR_DETAIL = 'detail';
public const META_LIST_TOTAL_RESULTS = 'totalResults';
public const META_LIST_RESOURCES = 'Resources';
public const META_LIST_START_INDEX = 'startIndex';
public const META_LIST_COUNT = 'count';
public const META_LIST_ITEMS_PER_PAGE = 'itemsPerPage';
public const META_SCHEMAS = 'schemas';
public const META_FILTER = 'filter';
public const META_TYPE_WORK = 'work';
public const META_PHONE_NUMBER_TYPE_MOBILE = 'mobile';
public const META_EMAIL_TYPE_HOME = 'home';
public const META_CONTAINER_NAME = 'name';
public const META_CONTAINER_EMAILS = 'emails';
public const RESOURCE_TYPE_USER = 'User';
public const USER_PATCH_ATTR_TITLE = 'title';
public const USER_PATCH_ATTR_ACTIVE = 'active';
public const USER_PATCH_ATTR_USERNAME = 'userName';
public const USER_PATCH_ATTR_FIRST_NAME = 'name.givenName';
public const USER_PATCH_ATTR_LAST_NAME = 'name.familyName';
public const USER_PATCH_ATTR_PRIMARY_EMAIL = 'emails[type eq "work"].value';
public const USER_PATCH_ATTR_SECONDARY_EMAIL = 'emails[type eq "home"].value';
public const USER_PATCH_ATTR_PHONE_NUMBER = 'phoneNumbers[type eq "work"].value';
public const USER_PATCH_ATTR_PHONE_NUMBER_MOBILE = 'phoneNumbers[type eq "mobile"].value';
public const USER_PATCH_ATTR_ROLES = 'roles';
public const USER_SUPPORTED_PATCH_ATTRIBUTES_MUTATORS = [
self::USER_PATCH_ATTR_TITLE => Mutators\Attributes\User\TitleAttr::class,
self::USER_PATCH_ATTR_ACTIVE => Mutators\Attributes\User\ActiveAttr::class,
self::USER_PATCH_ATTR_USERNAME => Mutators\Attributes\User\UsernameAttr::class,
self::USER_PATCH_ATTR_FIRST_NAME => Mutators\Attributes\User\FirstNameAttr::class,
self::USER_PATCH_ATTR_LAST_NAME => Mutators\Attributes\User\LastNameAttr::class,
self::USER_PATCH_ATTR_PRIMARY_EMAIL => Mutators\Attributes\User\PrimaryEmailAttr::class,
self::USER_PATCH_ATTR_SECONDARY_EMAIL => Mutators\Attributes\User\SecondaryEmailAttr::class,
self::USER_PATCH_ATTR_PHONE_NUMBER => Mutators\Attributes\User\PhoneNumberPrimaryAttr::class,
self::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE => Mutators\Attributes\User\PhoneNumberSecondaryAttr::class,
];
public const FILTER_OPERATOR_EQ = 'eq';
public const SUPPORTED_FILTERS_OPERATORS = [
self::FILTER_OPERATOR_EQ,
];
public const USER_SUPPORTED_FILTER_ATTRIBUTES_QUERY_BUILDERS = [
self::USER_FILTER_LOCAL_ID => Filters\User\LocalIdFilter::class,
self::USER_FILTER_USERNAME => Filters\User\UserNameFilter::class,
self::USER_FILTER_FIRST_NAME => Filters\User\FirstNameFilter::class,
self::USER_FILTER_LAST_NAME => Filters\User\LastNameFilter::class,
self::USER_FILTER_EXTERNAL_ID => Filters\User\ExternalIdFilter::class,
self::USER_FILTER_DISPLAY_NAME => Filters\User\DisplayNameFilter::class,
];
public const USER_FILTER_LOCAL_ID = 'id';
public const USER_FILTER_USERNAME = 'userName';
public const USER_FILTER_FIRST_NAME = 'givenName';
public const USER_FILTER_LAST_NAME = 'familyName';
public const USER_FILTER_EXTERNAL_ID = 'externalId';
public const USER_FILTER_DISPLAY_NAME = 'DisplayName';
public const SCIM_SUPPORTED_RESOURCES = [
self::USER_RESOURCE_PREFIX,
self::GROUP_RESOURCE_PREFIX,
];
public const USER_RESOURCE_PREFIX = 'Users';
public const GROUP_RESOURCE_PREFIX = 'Groups';
public const OUTGOING_SCHEMAS_SUPPORTED = [
self::OUTGOING_CORE_USER,
];
/**
* Outgoing supported schemas
*/
public const OUTGOING_CORE_USER = 'urn:ietf:params:scim:schemas:core:2.0:User';
public const OUTGOING_ERROR = 'urn:ietf:params:scim:api:messages:2.0:Error';
public const OUTGOING_LIST_RESPONSE = 'urn:ietf:params:scim:api:messages:2.0:ListResponse';
public const OUTGOING_CORE_GROUP = 'urn:ietf:params:scim:schemas:core:2.0:Group';
/**
* Groups resource constants
*/
public const GROUP_ATTRIBUTE_DISPLAY_NAME = 'displayName';
public const GROUP_ATTRIBUTE_EXTERNAL_ID = 'externalId';
public const GROUP_ATTRIBUTE_ID = 'id';
public const RESOURCE_TYPE_GROUP = 'Group';
public const GROUP_FILTER_QUERY_NAME = 'filter';
public const GROUP_EXCLUDED_ATTRIBUTES = 'excludedAttributes';
public const GROUP_SUPPORTED_FILTER_ATTRIBUTES = [
self::GROUP_ATTRIBUTE_DISPLAY_NAME,
];
public const GROUP_MEMBERS = 'members';
public const SUPPORTED_PATCH_OPERATIONS = [
self::META_OPERATIONS_ADD,
self::META_OPERATIONS_REPLACE,
self::META_OPERATIONS_REMOVE,
];
public const GROUP_PATCH_ATTRIBUTE_MEMBERS = 'members';
public const GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME = 'displayName';
public const GROUP_SUPPORTED_PATCH_ATTRIBUTES_MUTATORS = [
self::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME => Mutators\Attributes\Group\DisplayNameAttr::class,
self::GROUP_PATCH_ATTRIBUTE_MEMBERS => Mutators\Attributes\Group\MembersAttr::class,
];
public const GROUP_FILTER_DISPLAY_NAME = 'displayname';
public const GROUP_SUPPORTED_FILTER_ATTRIBUTES_QUERY_BUILDERS = [
self::GROUP_FILTER_DISPLAY_NAME => Filters\Group\DisplayNameFilter::class,
];
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53091
|
1861
|
5
|
2026-05-18T11:35:53.886147+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104153886_m2.jpg...
|
PhpStorm
|
faVsco.js – RoleAttr.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorTOOISWindo PhostormVIewINavicarecodeLaravelKeractorTOOISWindowmelpFV faVsco.js°9 master k >Proletey> D Builders> @ Mutators® ActivityController.php© CoreUserRequest.php• CrmOwnerResolver.phpconstants.onp© BaseService.php© SoftPhoneManager.php• RoleAttr.php x) © TextMessagingService.phpconstants.onp@ scimintenace.onpdeclare(scrict_cypes=l)hscimprovisioning.pnpW seeder• Sentry_ SerializerSettingsSidekicknamespace Jiminny Component Sclm Mutators Attributes User:17 TranscriotionSummarvSENEBETOIclass RoleAttr extends UserMutatororivate Rolerevosttorvinterrace Srolerevosttory:private RoleChangeLogger SroleChangeLogger;orivate UodatelserRolesActiion SuodatelserRolesAction:M Twilidv N Conference• M ConferenceHandlerv • ConferenceManager© SoftPhoneManager.php@ ConferenceCallbackHandler.php© ConferenceManager.php>MоTOpublic function __construct(User $user,string $valve,?\Jiminny\TeamScimDetails Sdetail,RoleRepositoryInterface $roleRepository,RoleChangeLogger $roleChangeLogger,UpdateUserRolesAction $updateUserRolesActionD f..3> M Event>D ExceptionD Job>D Resolver07 Servicepublic function doModification(): Userf.}private function parseRoles(string $value): arrayf….,c)Twilioconstants.phgprivate function' ab&igliRotest~ Accept Flle *= o Xe Reingt fle Vosdt.+,2 of 2files →Local ChangesLog x• Chances 7 tlles, uodatino.→ E Side-by-side viewer= env.local apo@cb4ebf0c config/logging.php© Constants.php app/Component/SCIM(C) CoreUserRequest.oho aoo/DTO/SCIMIAAD/Requesti© JiminnyDebugCommand.php app/Console/Commaphp loadina,oho confid'driver' =>'Level' => env('LOG_LEVEL', 'info'),'path' => storage_path('Logs/Laravel.Log'),© SoftPhoneManager.php app/Component/Twilio/Conference/ConferenceManagC) TeytMescacinaService.ohn ann/Services/Telenhonv~ Unversioned Files 9 files, updating..= env nikilocal annlE.env.other appC) CanAccocsAiRenortsTest nhn tests/Unit/Policiod© CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/RiE favicon.ico publicE ids.txt appTraw sal querv.sal ape© SimulateWebhooksCommand.php app/Console/Commands/Crm/HubspotM+ WEBHOOK FILTERING_IMPLEMENTATION.md apper' has been restored // Rollback // Configure... (16 minutes ago)ACCepT RepectE custom.logA console [STAGING] XE laravel.log4 SF [jiminny@localhost]A HS_Jocal [jiminny@localhost]A console [PROD]# console [euy Tx: Auto vS. jiminny_jupiter019 A19 V17 ^ Vselect * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.fcequency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expices.at >= now() OR r.expices.at IS NULL);select * from automated_report_results where ceportaid IN (18, 33);select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;select * from activity_searches where id = 10932;select * from activity_search_filters where activity_search_id = 10932;select * tromorder by 10 descresults order by id desc;automated reports where 10 IN (55select * from automated_report_results where ig IN (81);select * trom users where 1d IN 00055,15787,11 985071select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:suppont Dally • In zom100% 28• Mon 18 May 14:35:53U AskJiminnyReportActivityServiceTest~CascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..sedrcned class. Kole in ap.inougntror is1Analyze current selm implementation and role permission systemReview validation rules for roles and permissionsiDesign SCIM role assignment schemaImolement role mutator for SCIMAdd validation to SCIM role assianmentWrite unit tests for SCIM role managementUodate SCIM documentationkead scimProvisioning.onp and s otnertlles >Thought for 1s›Thought for 2s>Searched ROLE_RECORDERIROLE_VOICEIROLE_ANALYST|ROLE_LISTENER in app/Models/User.phpThought for 2sSearched PERMISSION ADMINPERMISSION MANAGER In app/ModelsRead RolesAndCrmOntionMismatchRule.ohdTllaseAsk anything (&AL)+ •code SWE-1.6Current versionReject allAccept all1ditterenceМАAAAAY"driver' = 'errorlog','Level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel. 10g'),'custom_channel' »> ['driver' →> 'single','path' => storage_path('logs/custom.log'),'Level' => env('LOG_LEVEL', 'info'),W Windsurf Toams 19-1UTF.8f 4 spaces...
|
NULL
|
2230765445516845819
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorTOOISWindo PhostormVIewINavicarecodeLaravelKeractorTOOISWindowmelpFV faVsco.js°9 master k >Proletey> D Builders> @ Mutators® ActivityController.php© CoreUserRequest.php• CrmOwnerResolver.phpconstants.onp© BaseService.php© SoftPhoneManager.php• RoleAttr.php x) © TextMessagingService.phpconstants.onp@ scimintenace.onpdeclare(scrict_cypes=l)hscimprovisioning.pnpW seeder• Sentry_ SerializerSettingsSidekicknamespace Jiminny Component Sclm Mutators Attributes User:17 TranscriotionSummarvSENEBETOIclass RoleAttr extends UserMutatororivate Rolerevosttorvinterrace Srolerevosttory:private RoleChangeLogger SroleChangeLogger;orivate UodatelserRolesActiion SuodatelserRolesAction:M Twilidv N Conference• M ConferenceHandlerv • ConferenceManager© SoftPhoneManager.php@ ConferenceCallbackHandler.php© ConferenceManager.php>MоTOpublic function __construct(User $user,string $valve,?\Jiminny\TeamScimDetails Sdetail,RoleRepositoryInterface $roleRepository,RoleChangeLogger $roleChangeLogger,UpdateUserRolesAction $updateUserRolesActionD f..3> M Event>D ExceptionD Job>D Resolver07 Servicepublic function doModification(): Userf.}private function parseRoles(string $value): arrayf….,c)Twilioconstants.phgprivate function' ab&igliRotest~ Accept Flle *= o Xe Reingt fle Vosdt.+,2 of 2files →Local ChangesLog x• Chances 7 tlles, uodatino.→ E Side-by-side viewer= env.local apo@cb4ebf0c config/logging.php© Constants.php app/Component/SCIM(C) CoreUserRequest.oho aoo/DTO/SCIMIAAD/Requesti© JiminnyDebugCommand.php app/Console/Commaphp loadina,oho confid'driver' =>'Level' => env('LOG_LEVEL', 'info'),'path' => storage_path('Logs/Laravel.Log'),© SoftPhoneManager.php app/Component/Twilio/Conference/ConferenceManagC) TeytMescacinaService.ohn ann/Services/Telenhonv~ Unversioned Files 9 files, updating..= env nikilocal annlE.env.other appC) CanAccocsAiRenortsTest nhn tests/Unit/Policiod© CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/RiE favicon.ico publicE ids.txt appTraw sal querv.sal ape© SimulateWebhooksCommand.php app/Console/Commands/Crm/HubspotM+ WEBHOOK FILTERING_IMPLEMENTATION.md apper' has been restored // Rollback // Configure... (16 minutes ago)ACCepT RepectE custom.logA console [STAGING] XE laravel.log4 SF [jiminny@localhost]A HS_Jocal [jiminny@localhost]A console [PROD]# console [euy Tx: Auto vS. jiminny_jupiter019 A19 V17 ^ Vselect * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.fcequency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expices.at >= now() OR r.expices.at IS NULL);select * from automated_report_results where ceportaid IN (18, 33);select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;select * from activity_searches where id = 10932;select * from activity_search_filters where activity_search_id = 10932;select * tromorder by 10 descresults order by id desc;automated reports where 10 IN (55select * from automated_report_results where ig IN (81);select * trom users where 1d IN 00055,15787,11 985071select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:suppont Dally • In zom100% 28• Mon 18 May 14:35:53U AskJiminnyReportActivityServiceTest~CascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..sedrcned class. Kole in ap.inougntror is1Analyze current selm implementation and role permission systemReview validation rules for roles and permissionsiDesign SCIM role assignment schemaImolement role mutator for SCIMAdd validation to SCIM role assianmentWrite unit tests for SCIM role managementUodate SCIM documentationkead scimProvisioning.onp and s otnertlles >Thought for 1s›Thought for 2s>Searched ROLE_RECORDERIROLE_VOICEIROLE_ANALYST|ROLE_LISTENER in app/Models/User.phpThought for 2sSearched PERMISSION ADMINPERMISSION MANAGER In app/ModelsRead RolesAndCrmOntionMismatchRule.ohdTllaseAsk anything (&AL)+ •code SWE-1.6Current versionReject allAccept all1ditterenceМАAAAAY"driver' = 'errorlog','Level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel. 10g'),'custom_channel' »> ['driver' →> 'single','path' => storage_path('logs/custom.log'),'Level' => env('LOG_LEVEL', 'info'),W Windsurf Toams 19-1UTF.8f 4 spaces...
|
53090
|
NULL
|
NULL
|
NULL
|
|
53092
|
1860
|
4
|
2026-05-18T11:35:55.974693+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104155974_m1.jpg...
|
PhpStorm
|
faVsco.js – RoleAttr.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82XIDOCKER (docker-compose)asticsearch"{"type": "log""@timestamp":"2026-05-18T11:35:49Z", "tags" : ["warning""monitoring"], "pid" :7,"Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11: 35:49Z", "tags" : ["warning"asticsearch", "monitoring"],"pid" :7, "message": "No living connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:35:49Z""tags": ["warning""licensing"], "pid" :7,"message" : "License informationcould not be obtained fromasticsearch dueto Error: No Livingconnections1 {"type" : "log","@timestamp": "2026-05-18T11:35:51Z""tags" : ["error"ticsearch", "data"], "pid":7, "message" : " [ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch: 9200"}docker_lamp_12026-05-18 11:35:46 Running ['artisan'docker_1amp_1"error":mailbox:text-relay:sync] {"invalid_request"docker_lamp_1field:@""error_description":"Invalid impersonation \u0026quot; sub\u0026quot;docker_lamp_11}kibana{"type" : "log","@timestamp": "2026-05-18T11:35:51Z", "tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}docker_lamp_1docker-1amp_1 /. 4s DONE1 '/usr/local/bin/php' 'artisan' mailbox:text-relay:sync › */proc/1/fd/1' 2>&1kibanaI {"type" : "log", "@timestamp" : "2026-05-18T11:35:51Z","tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:35:51Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}kibanaI {"type" : "log", "@timestamp" : "2026-05-18T11:35:51Z", "tags" : ["error"ins", "taskManager", "taskManager"], "pid" :7, "message" : "Failed to pollfor work: Error: NoLiving connections"}kibanaI {"type" : "log", "@timestamp" : "2026-05-18T11:35:53Z""tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}1 {"type" : "log","@timestamp": "2026-05-18T11:35:54Z","tags" : ["warning"asticsearch", "data"], "pid" :7,"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "data"], "pid" :7, "message": "No living connections"}kibana1 {"type": "log", "@timestamp" : "2026-05-18T11:35:54Z", "tags" : ["error", "plugins",, "taskManager",, "taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View ConfigEnable Watch•HomeDMsActivityFilesLaterMore-Jiminny ...Metrotrtencee# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario GeorgievNikolay IvanovLo James Graham *. Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 25 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:35:556 0O Files+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
-6152654402189991172
|
NULL
|
idle
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DE SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82XIDOCKER (docker-compose)asticsearch"{"type": "log""@timestamp":"2026-05-18T11:35:49Z", "tags" : ["warning""monitoring"], "pid" :7,"Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11: 35:49Z", "tags" : ["warning"asticsearch", "monitoring"],"pid" :7, "message": "No living connections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:35:49Z""tags": ["warning""licensing"], "pid" :7,"message" : "License informationcould not be obtained fromasticsearch dueto Error: No Livingconnections1 {"type" : "log","@timestamp": "2026-05-18T11:35:51Z""tags" : ["error"ticsearch", "data"], "pid":7, "message" : " [ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch: 9200"}docker_lamp_12026-05-18 11:35:46 Running ['artisan'docker_1amp_1"error":mailbox:text-relay:sync] {"invalid_request"docker_lamp_1field:@""error_description":"Invalid impersonation \u0026quot; sub\u0026quot;docker_lamp_11}kibana{"type" : "log","@timestamp": "2026-05-18T11:35:51Z", "tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}docker_lamp_1docker-1amp_1 /. 4s DONE1 '/usr/local/bin/php' 'artisan' mailbox:text-relay:sync › */proc/1/fd/1' 2>&1kibanaI {"type" : "log", "@timestamp" : "2026-05-18T11:35:51Z","tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:35:51Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}kibanaI {"type" : "log", "@timestamp" : "2026-05-18T11:35:51Z", "tags" : ["error"ins", "taskManager", "taskManager"], "pid" :7, "message" : "Failed to pollfor work: Error: NoLiving connections"}kibanaI {"type" : "log", "@timestamp" : "2026-05-18T11:35:53Z""tags" : ["error", "elasticsearch", "data"], "pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}1 {"type" : "log","@timestamp": "2026-05-18T11:35:54Z","tags" : ["warning"asticsearch", "data"], "pid" :7,"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "data"], "pid" :7, "message": "No living connections"}kibana1 {"type": "log", "@timestamp" : "2026-05-18T11:35:54Z", "tags" : ["error", "plugins",, "taskManager",, "taskManager"], "pid" :7, "message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktop• View ConfigEnable Watch•HomeDMsActivityFilesLaterMore-Jiminny ...Metrotrtencee# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario GeorgievNikolay IvanovLo James Graham *. Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 25 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:35:556 0O Files+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
53089
|
NULL
|
NULL
|
NULL
|
|
53093
|
1861
|
6
|
2026-05-18T11:36:21.120462+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104181120_m2.jpg...
|
PhpStorm
|
faVsco.js – ScimProvisioning.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings...
|
[{"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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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}]...
|
-4235983745889776938
|
-8204421443435123770
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js°9 master kProiectC ActivityController.ong>@ Builders© CoreUserRequest.phpScimProvisioning.php x © TextMessagingService.phpv D Mutatorsv D Attributesuse stmanny urulouenlaau request loecoroupbyquery>D Groupuse Jemanny uiu suln aau recuesc becuserbyuueryv D Useruse Jemanny Uiu suLn aau request racchoroupuse Jiminny evencs users usercreacedC) ACuiveAttr.ono© FirstNameAttr.phpuse Jiminny Models broup(C) LastNameAttr.phpuse Jiminny Models JobTitle:© PhoneNumberPrimaryAttr.phpJininny Models kolec) PhoneNumberSecondarvAttr.onuse Jiminny Models leam© PrimarvEmailAttr.phpJiminny Models userC) RoleAttr.ohpJiminny Contracts Acl Rolerepos1torvinterface:c) SecondarvemailAttr.onoC) TiitleAttronouse Illuminate Support\Facades\ Validator:(C) UsernameAttr.ohoJaminny Rules DependentRolesrule'C GrouoMutator.ohouse Jiminny Rules ListenerRoleRequtrestastenerFeaturerule:C UserMutator.phpuse Jaminny Actlons UodatelserRoLesaction:© GroupPatchOperation.php© UserPatchOperation.phpuse Jiminny DTO Roles \UserRolesSyncDTO;use Sentry:c) Conctants nhn© Sciminterface.phpclass ScimProvisioningc) ScimProvisioning.gnpM Seederpublic functionconstructD Sentryprivate readonly Dispatcher SeventDispatcher,nnivate neadoniv IlsenSenvice SucenSenvice.1M SerializenD Settingsprivate readonly RoleChangeLogger SroleChangeLoggerSidekick0 Teaminsights0 TimeMemoryMapperAccept File 2+public function validateusertameceptfie aream Rteat le bo0d+ 2of A filoc →Local Chanaes• Chances 7 tlles, uodatino.= env.local apo@cb4ebf0c config/logging.php• Constants.php app/Component/SCIM(C) CoreUserRequest.oho aoo/DTO/SCIMIAAD/Reauest'driver' =>lennonloa!@JiminnvDebuaCommand.oho app/Console/Comma'level' => env('LOG _LEVEL', 'info'),php loadina,oho confid@) SoftPhoneManager.phn ann/Comnonent/Twilio/Conference/ConferenceMa'path' => storage_path('logs/Laravel.log').C) TeytMescacinaService.ohn ann/Services/Telenhonv• Unversioned Files 10 filesi= env nikilocal annl= .env.other appC) CanAccoccAiRenortsTect nhn tests/Unit/Policiod@ CrostoMockAck liminnvDanortPacultCommand.nhnann/Concole/Commandc/Do favicon ico nublicFide tut anrT raw sal query.sql app© RoleAttr.php app/Component/SCIM/Mutators/Attributes/User© SimulateWebhooksCommand.php app/Console/Commands/Crm/HubspotM+ WEBHOOK_FILTERING_IMPLEMENTATION.md appr' hac heon rectored Il Pollback II Confiaure (16 minutec aao))Accept Reñect• suppont Dally • In 24 m100% 2• Mon 18 May 14:36:20AskJiminnyReportActivityServiceTest vFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..= custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X556562568Tx: Auto vselect * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team.id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;select * from activity searches where id = 10932select * trom activity search tilters where activity search 10 = 109521select * tromorder by 10 descautomated_report_results order by id desc;automated reports where 10 IN (55select * from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985071select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:Current vercion& console [PROD]# console leuyCascadeImplement Trial OwneGo jiminny_jupiter019 A19 V17 ^ V•Constants.phpThought for 1s>•Constants.phpThought for 1s›• Constants.phpRoleAttr.php|Thouahtso10 lasks done• Create RoleAttr mutator for SCIM© Update ScimProvisioning to handle roles during user creationThoucht for 2c s• ScimProvisioning.phpmllesetAsk anvthina (%4L)+ @code SWE-1.6new +170100 tokensReject allAccept allO901ditterence'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').W Windsurf Toams 1'1UTF.8io 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53094
|
1860
|
5
|
2026-05-18T11:36:27.468184+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104187468_m1.jpg...
|
PhpStorm
|
faVsco.js – ScimProvisioning.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\SCIM;
use Exception;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Validator;
use Jiminny\Component\Acl\RoleChangeContext;
use Jiminny\Component\Acl\RoleChangeLogger;
use Jiminny\Component\Acl\RoleChangeType;
use Jiminny\Component\SCIM\Builders\GroupFilterQueryBuilder;
use Jiminny\Component\SCIM\Builders\UsersFilterQueryBuilder;
use Jiminny\Component\SCIM\Mutators\GroupPatchOperation;
use Jiminny\Component\SCIM\Mutators\UserPatchOperation;
use Jiminny\DTO\SCIM\AAD\Request\CoreGroupRequest;
use Jiminny\DTO\SCIM\AAD\Request\CoreUserRequest;
use Jiminny\DTO\SCIM\AAD\Request\GetGroupByQuery;
use Jiminny\DTO\SCIM\AAD\Request\GetUserByQuery;
use Jiminny\DTO\SCIM\AAD\Request\PatchGroup;
use Jiminny\Events\Users\UserCreated;
use Jiminny\Models\Group;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Role;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\UserService;
use Jiminny\Contracts\Acl\RoleRepositoryInterface;
use Illuminate\Support\Facades\Validator;
use Jiminny\Rules\DependentRolesRule;
use Jiminny\Rules\ListenerRoleRequiresListenerFeatureRule;
use Jiminny\Actions\UpdateUserRolesAction;
use Jiminny\DTO\Roles\UserRolesSyncDTO;
use Sentry;
class ScimProvisioning
{
public function __construct(
private readonly Dispatcher $eventDispatcher,
private readonly UserService $userService,
private readonly RoleChangeLogger $roleChangeLogger,
) {
}
public function validateUserIdInput($id, Team $team): bool
{
if (! User::isUuid4($id)) {
return false;
}
$user = $this->getUserById($id);
return $user instanceof User && $team->isSame($user->getTeam());
}
public function updateUserAttributesIfChanged(User $user, CoreUserRequest $dto, Team $team): User
{
$actions = [];
if (! str_starts_with($user->getName(), $dto->getFirstName() . ' ')) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_FIRST_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getFirstName(),
);
}
if (! str_contains($user->getName(), ' ' . $dto->getLastName())) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_LAST_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getLastName(),
);
}
if ((! $user->isStatusActive() && in_array(
array_get(
$dto->getRawRequest(),
Constants::USER_PATCH_ATTR_ACTIVE,
),
[true, 'True', 1],
true,
)) ||
($user->isStatusActive() && in_array(
array_get(
$dto->getRawRequest(),
Constants::USER_PATCH_ATTR_ACTIVE,
),
[false, 'False', 0],
true,
))
) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_ACTIVE,
Constants::META_OPERATIONS_REPLACE,
$user->getStatus() === 1 ? 0 : 1,
);
}
if ((! $user->job()->exists() && ! in_array($dto->getTitle(), ['', null])) ||
(! in_array($dto->getTitle(), ['', null]) && $user->getJobTitle()->name !== $dto->getTitle())
) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_TITLE,
Constants::META_OPERATIONS_REPLACE,
$dto->getTitle(),
);
}
if ($user->getEmailAddress() !== $dto->getEmailPrimaryWork()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PRIMARY_EMAIL,
Constants::META_OPERATIONS_REPLACE,
$dto->getEmailPrimaryWork(),
);
}
if ($user->getSecondaryEmailAddress() !== $dto->getEmailOther()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_SECONDARY_EMAIL,
Constants::META_OPERATIONS_REPLACE,
$dto->getEmailOther(),
);
}
if ($user->getAttribute('phone') !== $dto->getPrimaryPhoneNumber()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PHONE_NUMBER,
Constants::META_OPERATIONS_REPLACE,
$dto->getPrimaryPhoneNumber() ?? '',
);
}
if ($user->getAttribute('secondary_phone') !== $dto->getSecondaryPhoneNumber()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE,
Constants::META_OPERATIONS_REPLACE,
$dto->getSecondaryPhoneNumber() ?? '',
);
}
// no username update support. We rely on email=username in general
foreach ($actions as $action) {
$user = (new UserPatchOperation($user, $action))->doUpdate();
}
return $user;
}
/** @return array{path: string, op: string, value: mixed} */
private function returnAction(string $path, string $op, mixed $value): array
{
return [
Constants::META_PATH => $path,
Constants::META_OP => $op,
Constants::META_VALUE => $value,
];
}
public function getUserById(string $id): ?User
{
return User::uuid($id, false)->first();
}
public function getGroupById(string $id): ?Group
{
return Group::uuid($id, false)->first();
}
public function validateGroupIdInput(string $id, Team $team): bool
{
if (! Group::isUuid4($id)) {
return false;
}
try {
/** @var Group|null $group */
$group = Group::uuid($id);
} catch (Exception) {
return false;
}
return $group instanceof Group && $team->isSame($group->getTeam());
}
public function patchUserOperation(array $actions, User $user): User
{
/**
* Supported operations
* on emails, phones, name, title, status except remove primaryEmail
*/
foreach ($actions as $action) {
$user = (new UserPatchOperation($user, $action))->doUpdate();
}
return $user;
}
public function translateQueryStringsToGroupModelQuery(GetGroupByQuery $dto, Team $team): Builder
{
$query = Group::query()
->where('team_id', $team->getId());
foreach ($dto->getFilters() as $filter) {
(new GroupFilterQueryBuilder($filter))->addToQuery($query);
}
return $query;
}
public function patchGroupOperation(PatchGroup $dto, Team $team, Group $group): bool
{
/**
* Supported operations
* - replace group name
* - add user member of the group
*/
foreach ($dto->getActions() as $action) {
$group = (new GroupPatchOperation($group, $action))->doUpdate();
}
return true;
}
public function translateQueryStringsToUserModelQuery(GetUserByQuery $dto, Team $team): Builder
{
$query = User::query()
->where('team_id', $team->getId());
foreach ($dto->getFilters() as $filter) {
(new UsersFilterQueryBuilder($filter))->addToQuery($query);
}
return $query;
}
public function createOrUpdateNewUser(CoreUserRequest $dto, Team $team): ?User
{
$phone = $dto->getPrimaryPhoneNumber();
$secondaryPhone = $dto->getSecondaryPhoneNumber();
if (! $this->isValidPhone($phone, $team->country_code)) {
$phone = null;
}
if (! $this->isValidPhone($secondaryPhone, $team->country_code)) {
$secondaryPhone = null;
}
$isActive = $dto->getActive();
$userUpdateData = [
'name' => $dto->getName(),
'secondary_email' => $dto->getEmailOther(),
'status' => $isActive ? User::STATUS_ACTIVE : User::STATUS_DEACTIVATED,
'language' => $team->default_language,
'timezone' => $team->default_timezone,
'country_code' => $team->country_code,
'conference_slug' => $this->userService->generateSlug($dto->getName()),
];
// Do not change phone if it is not provided
if ($phone !== null) {
$userUpdateData['phone'] = $phone;
}
// Do not change secondary phone if it is not provided
if ($secondaryPhone !== null) {
$userUpdateData['secondary_phone'] = $secondaryPhone;
}
try {
/** @var User $newUser */
$newUser = $team
->users()
->whereIn('users.status', [User::STATUS_ACTIVE, User::STATUS_DEACTIVATED])
->updateOrCreate(
['email' => $dto->getEmailPrimaryWork()],
$userUpdateData,
);
if (! $newUser->roles()->exists()) {
$newUser->addRole(User::ROLE_ANALYST);
/** @var Role $role */
$role = Role::query()->where(['name' => User::ROLE_ANALYST])->firstOrFail();
$this->roleChangeLogger->log(
activeUser: null,
affected: $newUser,
role: $role,
type: $newUser->wasRecentlyCreated
? RoleChangeType::CREATE_USER
: RoleChangeType::ADD_ROLE,
context: RoleChangeContext::SCIM,
);
}
if (! empty($dto->getTitle())) {
$jobTitle = $team->jobs()->where('name', $dto->getTitle())->first();
if ($jobTitle instanceof JobTitle) {
$newUser->update(['job_title_id' => $jobTitle->getId()]);
} else {
/** @var JobTitle $newJobTitle */
$newJobTitle = $team->jobs()->create([
'name' => $dto->getTitle(),
'is_selectable' => 0,
]);
$newUser->update(['job_title_id' => $newJobTitle->getId()]);
}
}
if ($newUser->wasRecentlyCreated) {
$this->eventDispatcher->dispatch(new UserCreated($newUser));
}
return $newUser;
} catch (Exception $exception) {
Sentry::captureException($exception);
return null;
}
}
private function isValidPhone(?string $number, string $countryCode): bool
{
if ($number === null) {
return true;
}
$validator = Validator::make(
['phone' => $number],
['phone' => ['phone:INTERNATIONAL,' . $countryCode]]
);
return ! $validator->fails();
}
public function deleteGroup($group): bool
{
$users = $group->members()->get();
foreach ($users as $user) {
$user->update(['group_id' => null]);
}
return $group->delete();
}
public function createNewGroup(CoreGroupRequest $dto, Team $team): ?Group
{
try {
/** @var Group|null $existingGroup */
$existingGroup = $team->groups()->withTrashed()->where('name', $dto->getDisplayName())->first();
if ($existingGroup) {
if ($existingGroup->getAttribute('deleted_at')) {
$existingGroup->restore();
}
return $existingGroup;
}
/** @var Group $newGroup */
$newGroup = $team->groups()->updateOrCreate(
['name' => $dto->getDisplayName()],
[
'slug' => str_replace(' ', '-', strtolower($dto->getDisplayName())),
'user_id' => $team->getOwner()->getId(),
'transcription_model_locale_id' => $team->getTranscriptionModelLocaleId(),
'default_timezone' => $team->getDefaultTimezone(),
'default_language' => $team->getDefaultLanguage(),
'country_code' => $team->getCountryCode(),
'playbook_id' => $team->getDefaultPlaybookId(),
],
);
return $newGroup;
} catch (Exception $exception) {
Sentry::captureException($exception);
return null;
}
}
public function updateGroupAttributesIfChanged(Group $group, CoreGroupRequest $dto, Team $team): Group
{
$actions = [];
if ($group->getName() !== $dto->getDisplayName()) {
$actions[] = $this->returnAction(
Constants::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getDisplayName(),
);
}
foreach ($actions as $action) {
$group = (new GroupPatchOperation($group, $action))->doUpdate();
}
return $group;
}
}...
|
[{"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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","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":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\SCIM;\n\nuse Exception;\nuse Illuminate\\Contracts\\Events\\Dispatcher;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Component\\Acl\\RoleChangeContext;\nuse Jiminny\\Component\\Acl\\RoleChangeLogger;\nuse Jiminny\\Component\\Acl\\RoleChangeType;\nuse Jiminny\\Component\\SCIM\\Builders\\GroupFilterQueryBuilder;\nuse Jiminny\\Component\\SCIM\\Builders\\UsersFilterQueryBuilder;\nuse Jiminny\\Component\\SCIM\\Mutators\\GroupPatchOperation;\nuse Jiminny\\Component\\SCIM\\Mutators\\UserPatchOperation;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\CoreGroupRequest;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\CoreUserRequest;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\GetGroupByQuery;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\GetUserByQuery;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\PatchGroup;\nuse Jiminny\\Events\\Users\\UserCreated;\nuse Jiminny\\Models\\Group;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\UserService;\nuse Jiminny\\Contracts\\Acl\\RoleRepositoryInterface;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Rules\\DependentRolesRule;\nuse Jiminny\\Rules\\ListenerRoleRequiresListenerFeatureRule;\nuse Jiminny\\Actions\\UpdateUserRolesAction;\nuse Jiminny\\DTO\\Roles\\UserRolesSyncDTO;\nuse Sentry;\n\nclass ScimProvisioning\n{\n public function __construct(\n private readonly Dispatcher $eventDispatcher,\n private readonly UserService $userService,\n private readonly RoleChangeLogger $roleChangeLogger,\n ) {\n }\n\n public function validateUserIdInput($id, Team $team): bool\n {\n if (! User::isUuid4($id)) {\n return false;\n }\n $user = $this->getUserById($id);\n\n return $user instanceof User && $team->isSame($user->getTeam());\n }\n\n public function updateUserAttributesIfChanged(User $user, CoreUserRequest $dto, Team $team): User\n {\n $actions = [];\n if (! str_starts_with($user->getName(), $dto->getFirstName() . ' ')) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_FIRST_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getFirstName(),\n );\n }\n if (! str_contains($user->getName(), ' ' . $dto->getLastName())) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_LAST_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getLastName(),\n );\n }\n if ((! $user->isStatusActive() && in_array(\n array_get(\n $dto->getRawRequest(),\n Constants::USER_PATCH_ATTR_ACTIVE,\n ),\n [true, 'True', 1],\n true,\n )) ||\n ($user->isStatusActive() && in_array(\n array_get(\n $dto->getRawRequest(),\n Constants::USER_PATCH_ATTR_ACTIVE,\n ),\n [false, 'False', 0],\n true,\n ))\n ) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_ACTIVE,\n Constants::META_OPERATIONS_REPLACE,\n $user->getStatus() === 1 ? 0 : 1,\n );\n }\n if ((! $user->job()->exists() && ! in_array($dto->getTitle(), ['', null])) ||\n (! in_array($dto->getTitle(), ['', null]) && $user->getJobTitle()->name !== $dto->getTitle())\n ) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_TITLE,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getTitle(),\n );\n }\n if ($user->getEmailAddress() !== $dto->getEmailPrimaryWork()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PRIMARY_EMAIL,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getEmailPrimaryWork(),\n );\n }\n if ($user->getSecondaryEmailAddress() !== $dto->getEmailOther()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_SECONDARY_EMAIL,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getEmailOther(),\n );\n }\n if ($user->getAttribute('phone') !== $dto->getPrimaryPhoneNumber()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PHONE_NUMBER,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getPrimaryPhoneNumber() ?? '',\n );\n }\n if ($user->getAttribute('secondary_phone') !== $dto->getSecondaryPhoneNumber()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getSecondaryPhoneNumber() ?? '',\n );\n }\n // no username update support. We rely on email=username in general\n\n foreach ($actions as $action) {\n $user = (new UserPatchOperation($user, $action))->doUpdate();\n }\n\n return $user;\n }\n\n /** @return array{path: string, op: string, value: mixed} */\n private function returnAction(string $path, string $op, mixed $value): array\n {\n return [\n Constants::META_PATH => $path,\n Constants::META_OP => $op,\n Constants::META_VALUE => $value,\n ];\n }\n\n public function getUserById(string $id): ?User\n {\n return User::uuid($id, false)->first();\n }\n\n public function getGroupById(string $id): ?Group\n {\n return Group::uuid($id, false)->first();\n }\n\n public function validateGroupIdInput(string $id, Team $team): bool\n {\n if (! Group::isUuid4($id)) {\n return false;\n }\n\n try {\n /** @var Group|null $group */\n $group = Group::uuid($id);\n } catch (Exception) {\n return false;\n }\n\n return $group instanceof Group && $team->isSame($group->getTeam());\n }\n\n public function patchUserOperation(array $actions, User $user): User\n {\n /**\n * Supported operations\n * on emails, phones, name, title, status except remove primaryEmail\n */\n foreach ($actions as $action) {\n $user = (new UserPatchOperation($user, $action))->doUpdate();\n }\n\n return $user;\n }\n\n public function translateQueryStringsToGroupModelQuery(GetGroupByQuery $dto, Team $team): Builder\n {\n $query = Group::query()\n ->where('team_id', $team->getId());\n\n foreach ($dto->getFilters() as $filter) {\n (new GroupFilterQueryBuilder($filter))->addToQuery($query);\n }\n\n return $query;\n }\n\n public function patchGroupOperation(PatchGroup $dto, Team $team, Group $group): bool\n {\n /**\n * Supported operations\n * - replace group name\n * - add user member of the group\n */\n foreach ($dto->getActions() as $action) {\n $group = (new GroupPatchOperation($group, $action))->doUpdate();\n }\n\n return true;\n }\n\n public function translateQueryStringsToUserModelQuery(GetUserByQuery $dto, Team $team): Builder\n {\n $query = User::query()\n ->where('team_id', $team->getId());\n\n foreach ($dto->getFilters() as $filter) {\n (new UsersFilterQueryBuilder($filter))->addToQuery($query);\n }\n\n return $query;\n }\n\n public function createOrUpdateNewUser(CoreUserRequest $dto, Team $team): ?User\n {\n $phone = $dto->getPrimaryPhoneNumber();\n $secondaryPhone = $dto->getSecondaryPhoneNumber();\n\n if (! $this->isValidPhone($phone, $team->country_code)) {\n $phone = null;\n }\n\n if (! $this->isValidPhone($secondaryPhone, $team->country_code)) {\n $secondaryPhone = null;\n }\n\n $isActive = $dto->getActive();\n\n $userUpdateData = [\n 'name' => $dto->getName(),\n 'secondary_email' => $dto->getEmailOther(),\n 'status' => $isActive ? User::STATUS_ACTIVE : User::STATUS_DEACTIVATED,\n 'language' => $team->default_language,\n 'timezone' => $team->default_timezone,\n 'country_code' => $team->country_code,\n 'conference_slug' => $this->userService->generateSlug($dto->getName()),\n ];\n\n // Do not change phone if it is not provided\n if ($phone !== null) {\n $userUpdateData['phone'] = $phone;\n }\n\n // Do not change secondary phone if it is not provided\n if ($secondaryPhone !== null) {\n $userUpdateData['secondary_phone'] = $secondaryPhone;\n }\n\n try {\n /** @var User $newUser */\n $newUser = $team\n ->users()\n ->whereIn('users.status', [User::STATUS_ACTIVE, User::STATUS_DEACTIVATED])\n ->updateOrCreate(\n ['email' => $dto->getEmailPrimaryWork()],\n $userUpdateData,\n );\n\n if (! $newUser->roles()->exists()) {\n $newUser->addRole(User::ROLE_ANALYST);\n /** @var Role $role */\n $role = Role::query()->where(['name' => User::ROLE_ANALYST])->firstOrFail();\n\n $this->roleChangeLogger->log(\n activeUser: null,\n affected: $newUser,\n role: $role,\n type: $newUser->wasRecentlyCreated\n ? RoleChangeType::CREATE_USER\n : RoleChangeType::ADD_ROLE,\n context: RoleChangeContext::SCIM,\n );\n }\n\n if (! empty($dto->getTitle())) {\n $jobTitle = $team->jobs()->where('name', $dto->getTitle())->first();\n if ($jobTitle instanceof JobTitle) {\n $newUser->update(['job_title_id' => $jobTitle->getId()]);\n } else {\n /** @var JobTitle $newJobTitle */\n $newJobTitle = $team->jobs()->create([\n 'name' => $dto->getTitle(),\n 'is_selectable' => 0,\n ]);\n $newUser->update(['job_title_id' => $newJobTitle->getId()]);\n }\n }\n\n if ($newUser->wasRecentlyCreated) {\n $this->eventDispatcher->dispatch(new UserCreated($newUser));\n }\n\n return $newUser;\n } catch (Exception $exception) {\n Sentry::captureException($exception);\n\n return null;\n }\n }\n\n private function isValidPhone(?string $number, string $countryCode): bool\n {\n if ($number === null) {\n return true;\n }\n\n $validator = Validator::make(\n ['phone' => $number],\n ['phone' => ['phone:INTERNATIONAL,' . $countryCode]]\n );\n\n return ! $validator->fails();\n }\n\n public function deleteGroup($group): bool\n {\n $users = $group->members()->get();\n\n foreach ($users as $user) {\n $user->update(['group_id' => null]);\n }\n\n return $group->delete();\n }\n\n public function createNewGroup(CoreGroupRequest $dto, Team $team): ?Group\n {\n try {\n /** @var Group|null $existingGroup */\n $existingGroup = $team->groups()->withTrashed()->where('name', $dto->getDisplayName())->first();\n\n if ($existingGroup) {\n if ($existingGroup->getAttribute('deleted_at')) {\n $existingGroup->restore();\n }\n\n return $existingGroup;\n }\n\n /** @var Group $newGroup */\n $newGroup = $team->groups()->updateOrCreate(\n ['name' => $dto->getDisplayName()],\n [\n 'slug' => str_replace(' ', '-', strtolower($dto->getDisplayName())),\n 'user_id' => $team->getOwner()->getId(),\n 'transcription_model_locale_id' => $team->getTranscriptionModelLocaleId(),\n 'default_timezone' => $team->getDefaultTimezone(),\n 'default_language' => $team->getDefaultLanguage(),\n 'country_code' => $team->getCountryCode(),\n 'playbook_id' => $team->getDefaultPlaybookId(),\n ],\n );\n\n return $newGroup;\n } catch (Exception $exception) {\n Sentry::captureException($exception);\n\n return null;\n }\n }\n\n public function updateGroupAttributesIfChanged(Group $group, CoreGroupRequest $dto, Team $team): Group\n {\n $actions = [];\n\n if ($group->getName() !== $dto->getDisplayName()) {\n $actions[] = $this->returnAction(\n Constants::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getDisplayName(),\n );\n }\n\n foreach ($actions as $action) {\n $group = (new GroupPatchOperation($group, $action))->doUpdate();\n }\n\n return $group;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\SCIM;\n\nuse Exception;\nuse Illuminate\\Contracts\\Events\\Dispatcher;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Component\\Acl\\RoleChangeContext;\nuse Jiminny\\Component\\Acl\\RoleChangeLogger;\nuse Jiminny\\Component\\Acl\\RoleChangeType;\nuse Jiminny\\Component\\SCIM\\Builders\\GroupFilterQueryBuilder;\nuse Jiminny\\Component\\SCIM\\Builders\\UsersFilterQueryBuilder;\nuse Jiminny\\Component\\SCIM\\Mutators\\GroupPatchOperation;\nuse Jiminny\\Component\\SCIM\\Mutators\\UserPatchOperation;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\CoreGroupRequest;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\CoreUserRequest;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\GetGroupByQuery;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\GetUserByQuery;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\PatchGroup;\nuse Jiminny\\Events\\Users\\UserCreated;\nuse Jiminny\\Models\\Group;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\UserService;\nuse Jiminny\\Contracts\\Acl\\RoleRepositoryInterface;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Rules\\DependentRolesRule;\nuse Jiminny\\Rules\\ListenerRoleRequiresListenerFeatureRule;\nuse Jiminny\\Actions\\UpdateUserRolesAction;\nuse Jiminny\\DTO\\Roles\\UserRolesSyncDTO;\nuse Sentry;\n\nclass ScimProvisioning\n{\n public function __construct(\n private readonly Dispatcher $eventDispatcher,\n private readonly UserService $userService,\n private readonly RoleChangeLogger $roleChangeLogger,\n ) {\n }\n\n public function validateUserIdInput($id, Team $team): bool\n {\n if (! User::isUuid4($id)) {\n return false;\n }\n $user = $this->getUserById($id);\n\n return $user instanceof User && $team->isSame($user->getTeam());\n }\n\n public function updateUserAttributesIfChanged(User $user, CoreUserRequest $dto, Team $team): User\n {\n $actions = [];\n if (! str_starts_with($user->getName(), $dto->getFirstName() . ' ')) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_FIRST_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getFirstName(),\n );\n }\n if (! str_contains($user->getName(), ' ' . $dto->getLastName())) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_LAST_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getLastName(),\n );\n }\n if ((! $user->isStatusActive() && in_array(\n array_get(\n $dto->getRawRequest(),\n Constants::USER_PATCH_ATTR_ACTIVE,\n ),\n [true, 'True', 1],\n true,\n )) ||\n ($user->isStatusActive() && in_array(\n array_get(\n $dto->getRawRequest(),\n Constants::USER_PATCH_ATTR_ACTIVE,\n ),\n [false, 'False', 0],\n true,\n ))\n ) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_ACTIVE,\n Constants::META_OPERATIONS_REPLACE,\n $user->getStatus() === 1 ? 0 : 1,\n );\n }\n if ((! $user->job()->exists() && ! in_array($dto->getTitle(), ['', null])) ||\n (! in_array($dto->getTitle(), ['', null]) && $user->getJobTitle()->name !== $dto->getTitle())\n ) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_TITLE,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getTitle(),\n );\n }\n if ($user->getEmailAddress() !== $dto->getEmailPrimaryWork()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PRIMARY_EMAIL,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getEmailPrimaryWork(),\n );\n }\n if ($user->getSecondaryEmailAddress() !== $dto->getEmailOther()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_SECONDARY_EMAIL,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getEmailOther(),\n );\n }\n if ($user->getAttribute('phone') !== $dto->getPrimaryPhoneNumber()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PHONE_NUMBER,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getPrimaryPhoneNumber() ?? '',\n );\n }\n if ($user->getAttribute('secondary_phone') !== $dto->getSecondaryPhoneNumber()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getSecondaryPhoneNumber() ?? '',\n );\n }\n // no username update support. We rely on email=username in general\n\n foreach ($actions as $action) {\n $user = (new UserPatchOperation($user, $action))->doUpdate();\n }\n\n return $user;\n }\n\n /** @return array{path: string, op: string, value: mixed} */\n private function returnAction(string $path, string $op, mixed $value): array\n {\n return [\n Constants::META_PATH => $path,\n Constants::META_OP => $op,\n Constants::META_VALUE => $value,\n ];\n }\n\n public function getUserById(string $id): ?User\n {\n return User::uuid($id, false)->first();\n }\n\n public function getGroupById(string $id): ?Group\n {\n return Group::uuid($id, false)->first();\n }\n\n public function validateGroupIdInput(string $id, Team $team): bool\n {\n if (! Group::isUuid4($id)) {\n return false;\n }\n\n try {\n /** @var Group|null $group */\n $group = Group::uuid($id);\n } catch (Exception) {\n return false;\n }\n\n return $group instanceof Group && $team->isSame($group->getTeam());\n }\n\n public function patchUserOperation(array $actions, User $user): User\n {\n /**\n * Supported operations\n * on emails, phones, name, title, status except remove primaryEmail\n */\n foreach ($actions as $action) {\n $user = (new UserPatchOperation($user, $action))->doUpdate();\n }\n\n return $user;\n }\n\n public function translateQueryStringsToGroupModelQuery(GetGroupByQuery $dto, Team $team): Builder\n {\n $query = Group::query()\n ->where('team_id', $team->getId());\n\n foreach ($dto->getFilters() as $filter) {\n (new GroupFilterQueryBuilder($filter))->addToQuery($query);\n }\n\n return $query;\n }\n\n public function patchGroupOperation(PatchGroup $dto, Team $team, Group $group): bool\n {\n /**\n * Supported operations\n * - replace group name\n * - add user member of the group\n */\n foreach ($dto->getActions() as $action) {\n $group = (new GroupPatchOperation($group, $action))->doUpdate();\n }\n\n return true;\n }\n\n public function translateQueryStringsToUserModelQuery(GetUserByQuery $dto, Team $team): Builder\n {\n $query = User::query()\n ->where('team_id', $team->getId());\n\n foreach ($dto->getFilters() as $filter) {\n (new UsersFilterQueryBuilder($filter))->addToQuery($query);\n }\n\n return $query;\n }\n\n public function createOrUpdateNewUser(CoreUserRequest $dto, Team $team): ?User\n {\n $phone = $dto->getPrimaryPhoneNumber();\n $secondaryPhone = $dto->getSecondaryPhoneNumber();\n\n if (! $this->isValidPhone($phone, $team->country_code)) {\n $phone = null;\n }\n\n if (! $this->isValidPhone($secondaryPhone, $team->country_code)) {\n $secondaryPhone = null;\n }\n\n $isActive = $dto->getActive();\n\n $userUpdateData = [\n 'name' => $dto->getName(),\n 'secondary_email' => $dto->getEmailOther(),\n 'status' => $isActive ? User::STATUS_ACTIVE : User::STATUS_DEACTIVATED,\n 'language' => $team->default_language,\n 'timezone' => $team->default_timezone,\n 'country_code' => $team->country_code,\n 'conference_slug' => $this->userService->generateSlug($dto->getName()),\n ];\n\n // Do not change phone if it is not provided\n if ($phone !== null) {\n $userUpdateData['phone'] = $phone;\n }\n\n // Do not change secondary phone if it is not provided\n if ($secondaryPhone !== null) {\n $userUpdateData['secondary_phone'] = $secondaryPhone;\n }\n\n try {\n /** @var User $newUser */\n $newUser = $team\n ->users()\n ->whereIn('users.status', [User::STATUS_ACTIVE, User::STATUS_DEACTIVATED])\n ->updateOrCreate(\n ['email' => $dto->getEmailPrimaryWork()],\n $userUpdateData,\n );\n\n if (! $newUser->roles()->exists()) {\n $newUser->addRole(User::ROLE_ANALYST);\n /** @var Role $role */\n $role = Role::query()->where(['name' => User::ROLE_ANALYST])->firstOrFail();\n\n $this->roleChangeLogger->log(\n activeUser: null,\n affected: $newUser,\n role: $role,\n type: $newUser->wasRecentlyCreated\n ? RoleChangeType::CREATE_USER\n : RoleChangeType::ADD_ROLE,\n context: RoleChangeContext::SCIM,\n );\n }\n\n if (! empty($dto->getTitle())) {\n $jobTitle = $team->jobs()->where('name', $dto->getTitle())->first();\n if ($jobTitle instanceof JobTitle) {\n $newUser->update(['job_title_id' => $jobTitle->getId()]);\n } else {\n /** @var JobTitle $newJobTitle */\n $newJobTitle = $team->jobs()->create([\n 'name' => $dto->getTitle(),\n 'is_selectable' => 0,\n ]);\n $newUser->update(['job_title_id' => $newJobTitle->getId()]);\n }\n }\n\n if ($newUser->wasRecentlyCreated) {\n $this->eventDispatcher->dispatch(new UserCreated($newUser));\n }\n\n return $newUser;\n } catch (Exception $exception) {\n Sentry::captureException($exception);\n\n return null;\n }\n }\n\n private function isValidPhone(?string $number, string $countryCode): bool\n {\n if ($number === null) {\n return true;\n }\n\n $validator = Validator::make(\n ['phone' => $number],\n ['phone' => ['phone:INTERNATIONAL,' . $countryCode]]\n );\n\n return ! $validator->fails();\n }\n\n public function deleteGroup($group): bool\n {\n $users = $group->members()->get();\n\n foreach ($users as $user) {\n $user->update(['group_id' => null]);\n }\n\n return $group->delete();\n }\n\n public function createNewGroup(CoreGroupRequest $dto, Team $team): ?Group\n {\n try {\n /** @var Group|null $existingGroup */\n $existingGroup = $team->groups()->withTrashed()->where('name', $dto->getDisplayName())->first();\n\n if ($existingGroup) {\n if ($existingGroup->getAttribute('deleted_at')) {\n $existingGroup->restore();\n }\n\n return $existingGroup;\n }\n\n /** @var Group $newGroup */\n $newGroup = $team->groups()->updateOrCreate(\n ['name' => $dto->getDisplayName()],\n [\n 'slug' => str_replace(' ', '-', strtolower($dto->getDisplayName())),\n 'user_id' => $team->getOwner()->getId(),\n 'transcription_model_locale_id' => $team->getTranscriptionModelLocaleId(),\n 'default_timezone' => $team->getDefaultTimezone(),\n 'default_language' => $team->getDefaultLanguage(),\n 'country_code' => $team->getCountryCode(),\n 'playbook_id' => $team->getDefaultPlaybookId(),\n ],\n );\n\n return $newGroup;\n } catch (Exception $exception) {\n Sentry::captureException($exception);\n\n return null;\n }\n }\n\n public function updateGroupAttributesIfChanged(Group $group, CoreGroupRequest $dto, Team $team): Group\n {\n $actions = [];\n\n if ($group->getName() !== $dto->getDisplayName()) {\n $actions[] = $this->returnAction(\n Constants::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getDisplayName(),\n );\n }\n\n foreach ($actions as $action) {\n $group = (new GroupPatchOperation($group, $action))->doUpdate();\n }\n\n return $group;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5292261031256091635
|
5517158551046434195
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\SCIM;
use Exception;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Validator;
use Jiminny\Component\Acl\RoleChangeContext;
use Jiminny\Component\Acl\RoleChangeLogger;
use Jiminny\Component\Acl\RoleChangeType;
use Jiminny\Component\SCIM\Builders\GroupFilterQueryBuilder;
use Jiminny\Component\SCIM\Builders\UsersFilterQueryBuilder;
use Jiminny\Component\SCIM\Mutators\GroupPatchOperation;
use Jiminny\Component\SCIM\Mutators\UserPatchOperation;
use Jiminny\DTO\SCIM\AAD\Request\CoreGroupRequest;
use Jiminny\DTO\SCIM\AAD\Request\CoreUserRequest;
use Jiminny\DTO\SCIM\AAD\Request\GetGroupByQuery;
use Jiminny\DTO\SCIM\AAD\Request\GetUserByQuery;
use Jiminny\DTO\SCIM\AAD\Request\PatchGroup;
use Jiminny\Events\Users\UserCreated;
use Jiminny\Models\Group;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Role;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\UserService;
use Jiminny\Contracts\Acl\RoleRepositoryInterface;
use Illuminate\Support\Facades\Validator;
use Jiminny\Rules\DependentRolesRule;
use Jiminny\Rules\ListenerRoleRequiresListenerFeatureRule;
use Jiminny\Actions\UpdateUserRolesAction;
use Jiminny\DTO\Roles\UserRolesSyncDTO;
use Sentry;
class ScimProvisioning
{
public function __construct(
private readonly Dispatcher $eventDispatcher,
private readonly UserService $userService,
private readonly RoleChangeLogger $roleChangeLogger,
) {
}
public function validateUserIdInput($id, Team $team): bool
{
if (! User::isUuid4($id)) {
return false;
}
$user = $this->getUserById($id);
return $user instanceof User && $team->isSame($user->getTeam());
}
public function updateUserAttributesIfChanged(User $user, CoreUserRequest $dto, Team $team): User
{
$actions = [];
if (! str_starts_with($user->getName(), $dto->getFirstName() . ' ')) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_FIRST_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getFirstName(),
);
}
if (! str_contains($user->getName(), ' ' . $dto->getLastName())) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_LAST_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getLastName(),
);
}
if ((! $user->isStatusActive() && in_array(
array_get(
$dto->getRawRequest(),
Constants::USER_PATCH_ATTR_ACTIVE,
),
[true, 'True', 1],
true,
)) ||
($user->isStatusActive() && in_array(
array_get(
$dto->getRawRequest(),
Constants::USER_PATCH_ATTR_ACTIVE,
),
[false, 'False', 0],
true,
))
) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_ACTIVE,
Constants::META_OPERATIONS_REPLACE,
$user->getStatus() === 1 ? 0 : 1,
);
}
if ((! $user->job()->exists() && ! in_array($dto->getTitle(), ['', null])) ||
(! in_array($dto->getTitle(), ['', null]) && $user->getJobTitle()->name !== $dto->getTitle())
) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_TITLE,
Constants::META_OPERATIONS_REPLACE,
$dto->getTitle(),
);
}
if ($user->getEmailAddress() !== $dto->getEmailPrimaryWork()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PRIMARY_EMAIL,
Constants::META_OPERATIONS_REPLACE,
$dto->getEmailPrimaryWork(),
);
}
if ($user->getSecondaryEmailAddress() !== $dto->getEmailOther()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_SECONDARY_EMAIL,
Constants::META_OPERATIONS_REPLACE,
$dto->getEmailOther(),
);
}
if ($user->getAttribute('phone') !== $dto->getPrimaryPhoneNumber()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PHONE_NUMBER,
Constants::META_OPERATIONS_REPLACE,
$dto->getPrimaryPhoneNumber() ?? '',
);
}
if ($user->getAttribute('secondary_phone') !== $dto->getSecondaryPhoneNumber()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE,
Constants::META_OPERATIONS_REPLACE,
$dto->getSecondaryPhoneNumber() ?? '',
);
}
// no username update support. We rely on email=username in general
foreach ($actions as $action) {
$user = (new UserPatchOperation($user, $action))->doUpdate();
}
return $user;
}
/** @return array{path: string, op: string, value: mixed} */
private function returnAction(string $path, string $op, mixed $value): array
{
return [
Constants::META_PATH => $path,
Constants::META_OP => $op,
Constants::META_VALUE => $value,
];
}
public function getUserById(string $id): ?User
{
return User::uuid($id, false)->first();
}
public function getGroupById(string $id): ?Group
{
return Group::uuid($id, false)->first();
}
public function validateGroupIdInput(string $id, Team $team): bool
{
if (! Group::isUuid4($id)) {
return false;
}
try {
/** @var Group|null $group */
$group = Group::uuid($id);
} catch (Exception) {
return false;
}
return $group instanceof Group && $team->isSame($group->getTeam());
}
public function patchUserOperation(array $actions, User $user): User
{
/**
* Supported operations
* on emails, phones, name, title, status except remove primaryEmail
*/
foreach ($actions as $action) {
$user = (new UserPatchOperation($user, $action))->doUpdate();
}
return $user;
}
public function translateQueryStringsToGroupModelQuery(GetGroupByQuery $dto, Team $team): Builder
{
$query = Group::query()
->where('team_id', $team->getId());
foreach ($dto->getFilters() as $filter) {
(new GroupFilterQueryBuilder($filter))->addToQuery($query);
}
return $query;
}
public function patchGroupOperation(PatchGroup $dto, Team $team, Group $group): bool
{
/**
* Supported operations
* - replace group name
* - add user member of the group
*/
foreach ($dto->getActions() as $action) {
$group = (new GroupPatchOperation($group, $action))->doUpdate();
}
return true;
}
public function translateQueryStringsToUserModelQuery(GetUserByQuery $dto, Team $team): Builder
{
$query = User::query()
->where('team_id', $team->getId());
foreach ($dto->getFilters() as $filter) {
(new UsersFilterQueryBuilder($filter))->addToQuery($query);
}
return $query;
}
public function createOrUpdateNewUser(CoreUserRequest $dto, Team $team): ?User
{
$phone = $dto->getPrimaryPhoneNumber();
$secondaryPhone = $dto->getSecondaryPhoneNumber();
if (! $this->isValidPhone($phone, $team->country_code)) {
$phone = null;
}
if (! $this->isValidPhone($secondaryPhone, $team->country_code)) {
$secondaryPhone = null;
}
$isActive = $dto->getActive();
$userUpdateData = [
'name' => $dto->getName(),
'secondary_email' => $dto->getEmailOther(),
'status' => $isActive ? User::STATUS_ACTIVE : User::STATUS_DEACTIVATED,
'language' => $team->default_language,
'timezone' => $team->default_timezone,
'country_code' => $team->country_code,
'conference_slug' => $this->userService->generateSlug($dto->getName()),
];
// Do not change phone if it is not provided
if ($phone !== null) {
$userUpdateData['phone'] = $phone;
}
// Do not change secondary phone if it is not provided
if ($secondaryPhone !== null) {
$userUpdateData['secondary_phone'] = $secondaryPhone;
}
try {
/** @var User $newUser */
$newUser = $team
->users()
->whereIn('users.status', [User::STATUS_ACTIVE, User::STATUS_DEACTIVATED])
->updateOrCreate(
['email' => $dto->getEmailPrimaryWork()],
$userUpdateData,
);
if (! $newUser->roles()->exists()) {
$newUser->addRole(User::ROLE_ANALYST);
/** @var Role $role */
$role = Role::query()->where(['name' => User::ROLE_ANALYST])->firstOrFail();
$this->roleChangeLogger->log(
activeUser: null,
affected: $newUser,
role: $role,
type: $newUser->wasRecentlyCreated
? RoleChangeType::CREATE_USER
: RoleChangeType::ADD_ROLE,
context: RoleChangeContext::SCIM,
);
}
if (! empty($dto->getTitle())) {
$jobTitle = $team->jobs()->where('name', $dto->getTitle())->first();
if ($jobTitle instanceof JobTitle) {
$newUser->update(['job_title_id' => $jobTitle->getId()]);
} else {
/** @var JobTitle $newJobTitle */
$newJobTitle = $team->jobs()->create([
'name' => $dto->getTitle(),
'is_selectable' => 0,
]);
$newUser->update(['job_title_id' => $newJobTitle->getId()]);
}
}
if ($newUser->wasRecentlyCreated) {
$this->eventDispatcher->dispatch(new UserCreated($newUser));
}
return $newUser;
} catch (Exception $exception) {
Sentry::captureException($exception);
return null;
}
}
private function isValidPhone(?string $number, string $countryCode): bool
{
if ($number === null) {
return true;
}
$validator = Validator::make(
['phone' => $number],
['phone' => ['phone:INTERNATIONAL,' . $countryCode]]
);
return ! $validator->fails();
}
public function deleteGroup($group): bool
{
$users = $group->members()->get();
foreach ($users as $user) {
$user->update(['group_id' => null]);
}
return $group->delete();
}
public function createNewGroup(CoreGroupRequest $dto, Team $team): ?Group
{
try {
/** @var Group|null $existingGroup */
$existingGroup = $team->groups()->withTrashed()->where('name', $dto->getDisplayName())->first();
if ($existingGroup) {
if ($existingGroup->getAttribute('deleted_at')) {
$existingGroup->restore();
}
return $existingGroup;
}
/** @var Group $newGroup */
$newGroup = $team->groups()->updateOrCreate(
['name' => $dto->getDisplayName()],
[
'slug' => str_replace(' ', '-', strtolower($dto->getDisplayName())),
'user_id' => $team->getOwner()->getId(),
'transcription_model_locale_id' => $team->getTranscriptionModelLocaleId(),
'default_timezone' => $team->getDefaultTimezone(),
'default_language' => $team->getDefaultLanguage(),
'country_code' => $team->getCountryCode(),
'playbook_id' => $team->getDefaultPlaybookId(),
],
);
return $newGroup;
} catch (Exception $exception) {
Sentry::captureException($exception);
return null;
}
}
public function updateGroupAttributesIfChanged(Group $group, CoreGroupRequest $dto, Team $team): Group
{
$actions = [];
if ($group->getName() !== $dto->getDisplayName()) {
$actions[] = $this->returnAction(
Constants::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getDisplayName(),
);
}
foreach ($actions as $action) {
$group = (new GroupPatchOperation($group, $action))->doUpdate();
}
return $group;
}
}...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53095
|
1861
|
7
|
2026-05-18T11:36:53.236628+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104213236_m2.jpg...
|
iTerm2
|
NULL
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js°9 master kProiect vC ActivityController.ongC BaseService.onp>@ Buildersv D Mutatorsv D Attributes>D Groupv D User© CoreUserRequest.phpconstants.onp© ScimProvisioning.php x © TextMessagingService.phpuse Jiminny DTO\SCIM\AAD\Request\GetUserByQuery:use Jiminny uru ouen aau request racchbroupuse siminnyevents lusers lusertreaced,C) ACuiveAttr.ono© FirstNameAttr.phpuse Jiminny models broupuse Jiminny Models \JobTitle:(C) LastNameAttr.php© PhoneNumberPrimaryAttr.phpc) PhoneNumberSecondarvAttr.on© PrimaryEmailAttr.php(C) RoleAttr.ohp© SecondarvEmailAttr.ohnJiminny Models kole.use Jiminny Models leamuse Jiminny Models user.use Jiminny services userserviceJiminny concracts AcL koLekepoS1coryIncertaceise Iuluminare supporc racades vauloaror.C) TiitleAttrono(C) UsernameAttr.ohoC GrouoMutator.ohouse Jiminny Actions UpdateUserRolesaction:C UserMutator.php© GroupPatchOperation.php© UserPatchOperation.phpc) Conctants nhnuse Sentryclass Scimprovisioning© Sciminterface.phpscimrrovisionine.onpM Seederpublic function __construct(orivate readonlv Disnatcher SeventDisoatcher.D Sentry1M Serializenorivate readonlv UserService SuserService..D SettingsSidekickprivate readonly RoleChangeLogger SroleChangeLogger,private readonly RoleRepositoryInterface $roleRepository,private readonly UpdateUserRolesAction $updateUserRolesAction,{...}0 Teaminsights0 TimeMemoryMapperv Accent File se4X Reject File t&apublic function validateUserIdInput(Sid, Team Steam): bool{...}Local Chanaes• Chandes & tlles= env.local apo@ Constants.php app/Component/SCiMl@cb4ebf0c config/logging.php(C) CoreUserRequest.oho aoo/DTO/SCIMIAAD/[EMAIL] app/Console/Comma'driver' =>"ennonlod!'level' => env('LOG _LEVEL', 'info'),php loadina,oho confid'path' => storage_path('logs/Laravel.log').© ScimProvisioning.php app/Component/SCIM@) SoftPhoneManager.php app/Component/Twilio/Conference/ConferenceManag@ TeytMessaginaService.nhn ann/Services/TelenhonvUnversioned Files 10 filesE .env.nikilocal appenv.other ann(8 CanAcceccAiDonorteTact nhn toctell Init/Doliciod© CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Rf fovicon icn nubli=ids.txt appTa raw sal query.sal app© RoleAttr.php app/Component/SCIM/Mutators/Attributes/User© SimulateWebhooksCommand.php app/Console/Commands/Crm/HubspotMWCOLIOOK CI TEDING IMDI CAACNITATIONI md onrWorkcnace accociated with hranch 'macter' hac heon rectored /l Pollback II Confiaure (17 minutes aaolACcept Renect5621— 564565568=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] XTx: Auto vlere cri couT Luuraqiontue tanu UEULaVUUUK LE wO:select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'andn status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;select * from activity searches where id = 10932select * trom activity search tilters where activity search 10 = 109521select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55select * from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985011select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:• suppont Dally • In 24 m100% 2• Mon 18 May 14:36:52AskJiminnyReportActivityServiceTest v& console [PROD]& console [EU]CascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..Go jiminny_jupiterRoleAttr.php019 A19 V17 ^ Vtnougntso10 tasks done• Create RoleAttr mutator for SCIMUpdate scimprovisioning to nandle roles auring user creationThought for ZsScimProvisioning.phpThoughtsScimProvisioning.onoThouaht for 1sScimProvisioning.phpThoughtsScimProvisioning.php+1-15|Ask anvthina (%4L)+ @code SWE-1.6Reject allAccept allO901ditterenceCurrent vercion'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').W Windsurf Toams 20-1UTF.8io 4 spaces...
|
NULL
|
-636197509570280488
|
NULL
|
idle
|
ocr
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js°9 master kProiect vC ActivityController.ongC BaseService.onp>@ Buildersv D Mutatorsv D Attributes>D Groupv D User© CoreUserRequest.phpconstants.onp© ScimProvisioning.php x © TextMessagingService.phpuse Jiminny DTO\SCIM\AAD\Request\GetUserByQuery:use Jiminny uru ouen aau request racchbroupuse siminnyevents lusers lusertreaced,C) ACuiveAttr.ono© FirstNameAttr.phpuse Jiminny models broupuse Jiminny Models \JobTitle:(C) LastNameAttr.php© PhoneNumberPrimaryAttr.phpc) PhoneNumberSecondarvAttr.on© PrimaryEmailAttr.php(C) RoleAttr.ohp© SecondarvEmailAttr.ohnJiminny Models kole.use Jiminny Models leamuse Jiminny Models user.use Jiminny services userserviceJiminny concracts AcL koLekepoS1coryIncertaceise Iuluminare supporc racades vauloaror.C) TiitleAttrono(C) UsernameAttr.ohoC GrouoMutator.ohouse Jiminny Actions UpdateUserRolesaction:C UserMutator.php© GroupPatchOperation.php© UserPatchOperation.phpc) Conctants nhnuse Sentryclass Scimprovisioning© Sciminterface.phpscimrrovisionine.onpM Seederpublic function __construct(orivate readonlv Disnatcher SeventDisoatcher.D Sentry1M Serializenorivate readonlv UserService SuserService..D SettingsSidekickprivate readonly RoleChangeLogger SroleChangeLogger,private readonly RoleRepositoryInterface $roleRepository,private readonly UpdateUserRolesAction $updateUserRolesAction,{...}0 Teaminsights0 TimeMemoryMapperv Accent File se4X Reject File t&apublic function validateUserIdInput(Sid, Team Steam): bool{...}Local Chanaes• Chandes & tlles= env.local apo@ Constants.php app/Component/SCiMl@cb4ebf0c config/logging.php(C) CoreUserRequest.oho aoo/DTO/SCIMIAAD/[EMAIL] app/Console/Comma'driver' =>"ennonlod!'level' => env('LOG _LEVEL', 'info'),php loadina,oho confid'path' => storage_path('logs/Laravel.log').© ScimProvisioning.php app/Component/SCIM@) SoftPhoneManager.php app/Component/Twilio/Conference/ConferenceManag@ TeytMessaginaService.nhn ann/Services/TelenhonvUnversioned Files 10 filesE .env.nikilocal appenv.other ann(8 CanAcceccAiDonorteTact nhn toctell Init/Doliciod© CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Rf fovicon icn nubli=ids.txt appTa raw sal query.sal app© RoleAttr.php app/Component/SCIM/Mutators/Attributes/User© SimulateWebhooksCommand.php app/Console/Commands/Crm/HubspotMWCOLIOOK CI TEDING IMDI CAACNITATIONI md onrWorkcnace accociated with hranch 'macter' hac heon rectored /l Pollback II Confiaure (17 minutes aaolACcept Renect5621— 564565568=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] XTx: Auto vlere cri couT Luuraqiontue tanu UEULaVUUUK LE wO:select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'andn status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;select * from activity searches where id = 10932select * trom activity search tilters where activity search 10 = 109521select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55select * from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985011select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:• suppont Dally • In 24 m100% 2• Mon 18 May 14:36:52AskJiminnyReportActivityServiceTest v& console [PROD]& console [EU]CascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..Go jiminny_jupiterRoleAttr.php019 A19 V17 ^ Vtnougntso10 tasks done• Create RoleAttr mutator for SCIMUpdate scimprovisioning to nandle roles auring user creationThought for ZsScimProvisioning.phpThoughtsScimProvisioning.onoThouaht for 1sScimProvisioning.phpThoughtsScimProvisioning.php+1-15|Ask anvthina (%4L)+ @code SWE-1.6Reject allAccept allO901ditterenceCurrent vercion'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').W Windsurf Toams 20-1UTF.8io 4 spaces...
|
53093
|
NULL
|
NULL
|
NULL
|
|
53096
|
1860
|
6
|
2026-05-18T11:36:57.729051+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104217729_m1.jpg...
|
PhpStorm
|
faVsco.js – ScimProvisioning.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\SCIM;
use Exception;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Validator;
use Jiminny\Component\Acl\RoleChangeContext;
use Jiminny\Component\Acl\RoleChangeLogger;
use Jiminny\Component\Acl\RoleChangeType;
use Jiminny\Component\SCIM\Builders\GroupFilterQueryBuilder;
use Jiminny\Component\SCIM\Builders\UsersFilterQueryBuilder;
use Jiminny\Component\SCIM\Mutators\GroupPatchOperation;
use Jiminny\Component\SCIM\Mutators\UserPatchOperation;
use Jiminny\DTO\SCIM\AAD\Request\CoreGroupRequest;
use Jiminny\DTO\SCIM\AAD\Request\CoreUserRequest;
use Jiminny\DTO\SCIM\AAD\Request\GetGroupByQuery;
use Jiminny\DTO\SCIM\AAD\Request\GetUserByQuery;
use Jiminny\DTO\SCIM\AAD\Request\PatchGroup;
use Jiminny\Events\Users\UserCreated;
use Jiminny\Models\Group;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Role;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\UserService;
use Jiminny\Contracts\Acl\RoleRepositoryInterface;
use Illuminate\Support\Facades\Validator;
use Jiminny\Rules\DependentRolesRule;
use Jiminny\Rules\ListenerRoleRequiresListenerFeatureRule;
use Jiminny\Actions\UpdateUserRolesAction;
use Jiminny\DTO\Roles\UserRolesSyncDTO;
use Sentry;
class ScimProvisioning
{
public function __construct(
private readonly Dispatcher $eventDispatcher,
private readonly UserService $userService,
private readonly RoleChangeLogger $roleChangeLogger,
private readonly RoleRepositoryInterface $roleRepository,
private readonly UpdateUserRolesAction $updateUserRolesAction,
) {
}
public function validateUserIdInput($id, Team $team): bool
{
if (! User::isUuid4($id)) {
return false;
}
$user = $this->getUserById($id);
return $user instanceof User && $team->isSame($user->getTeam());
}
public function updateUserAttributesIfChanged(User $user, CoreUserRequest $dto, Team $team): User
{
$actions = [];
if (! str_starts_with($user->getName(), $dto->getFirstName() . ' ')) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_FIRST_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getFirstName(),
);
}
if (! str_contains($user->getName(), ' ' . $dto->getLastName())) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_LAST_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getLastName(),
);
}
if ((! $user->isStatusActive() && in_array(
array_get(
$dto->getRawRequest(),
Constants::USER_PATCH_ATTR_ACTIVE,
),
[true, 'True', 1],
true,
)) ||
($user->isStatusActive() && in_array(
array_get(
$dto->getRawRequest(),
Constants::USER_PATCH_ATTR_ACTIVE,
),
[false, 'False', 0],
true,
))
) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_ACTIVE,
Constants::META_OPERATIONS_REPLACE,
$user->getStatus() === 1 ? 0 : 1,
);
}
if ((! $user->job()->exists() && ! in_array($dto->getTitle(), ['', null])) ||
(! in_array($dto->getTitle(), ['', null]) && $user->getJobTitle()->name !== $dto->getTitle())
) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_TITLE,
Constants::META_OPERATIONS_REPLACE,
$dto->getTitle(),
);
}
if ($user->getEmailAddress() !== $dto->getEmailPrimaryWork()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PRIMARY_EMAIL,
Constants::META_OPERATIONS_REPLACE,
$dto->getEmailPrimaryWork(),
);
}
if ($user->getSecondaryEmailAddress() !== $dto->getEmailOther()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_SECONDARY_EMAIL,
Constants::META_OPERATIONS_REPLACE,
$dto->getEmailOther(),
);
}
if ($user->getAttribute('phone') !== $dto->getPrimaryPhoneNumber()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PHONE_NUMBER,
Constants::META_OPERATIONS_REPLACE,
$dto->getPrimaryPhoneNumber() ?? '',
);
}
if ($user->getAttribute('secondary_phone') !== $dto->getSecondaryPhoneNumber()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE,
Constants::META_OPERATIONS_REPLACE,
$dto->getSecondaryPhoneNumber() ?? '',
);
}
// no username update support. We rely on email=username in general
foreach ($actions as $action) {
$user = (new UserPatchOperation($user, $action))->doUpdate();
}
return $user;
}
/** @return array{path: string, op: string, value: mixed} */
private function returnAction(string $path, string $op, mixed $value): array
{
return [
Constants::META_PATH => $path,
Constants::META_OP => $op,
Constants::META_VALUE => $value,
];
}
public function getUserById(string $id): ?User
{
return User::uuid($id, false)->first();
}
public function getGroupById(string $id): ?Group
{
return Group::uuid($id, false)->first();
}
public function validateGroupIdInput(string $id, Team $team): bool
{
if (! Group::isUuid4($id)) {
return false;
}
try {
/** @var Group|null $group */
$group = Group::uuid($id);
} catch (Exception) {
return false;
}
return $group instanceof Group && $team->isSame($group->getTeam());
}
public function patchUserOperation(array $actions, User $user): User
{
/**
* Supported operations
* on emails, phones, name, title, status except remove primaryEmail
*/
foreach ($actions as $action) {
$user = (new UserPatchOperation($user, $action))->doUpdate();
}
return $user;
}
public function translateQueryStringsToGroupModelQuery(GetGroupByQuery $dto, Team $team): Builder
{
$query = Group::query()
->where('team_id', $team->getId());
foreach ($dto->getFilters() as $filter) {
(new GroupFilterQueryBuilder($filter))->addToQuery($query);
}
return $query;
}
public function patchGroupOperation(PatchGroup $dto, Team $team, Group $group): bool
{
/**
* Supported operations
* - replace group name
* - add user member of the group
*/
foreach ($dto->getActions() as $action) {
$group = (new GroupPatchOperation($group, $action))->doUpdate();
}
return true;
}
public function translateQueryStringsToUserModelQuery(GetUserByQuery $dto, Team $team): Builder
{
$query = User::query()
->where('team_id', $team->getId());
foreach ($dto->getFilters() as $filter) {
(new UsersFilterQueryBuilder($filter))->addToQuery($query);
}
return $query;
}
public function createOrUpdateNewUser(CoreUserRequest $dto, Team $team): ?User
{
$phone = $dto->getPrimaryPhoneNumber();
$secondaryPhone = $dto->getSecondaryPhoneNumber();
if (! $this->isValidPhone($phone, $team->country_code)) {
$phone = null;
}
if (! $this->isValidPhone($secondaryPhone, $team->country_code)) {
$secondaryPhone = null;
}
$isActive = $dto->getActive();
$userUpdateData = [
'name' => $dto->getName(),
'secondary_email' => $dto->getEmailOther(),
'status' => $isActive ? User::STATUS_ACTIVE : User::STATUS_DEACTIVATED,
'language' => $team->default_language,
'timezone' => $team->default_timezone,
'country_code' => $team->country_code,
'conference_slug' => $this->userService->generateSlug($dto->getName()),
];
// Do not change phone if it is not provided
if ($phone !== null) {
$userUpdateData['phone'] = $phone;
}
// Do not change secondary phone if it is not provided
if ($secondaryPhone !== null) {
$userUpdateData['secondary_phone'] = $secondaryPhone;
}
try {
/** @var User $newUser */
$newUser = $team
->users()
->whereIn('users.status', [User::STATUS_ACTIVE, User::STATUS_DEACTIVATED])
->updateOrCreate(
['email' => $dto->getEmailPrimaryWork()],
$userUpdateData,
);
$this->assignRolesFromScim($newUser, $dto, $team);
if (! empty($dto->getTitle())) {
$jobTitle = $team->jobs()->where('name', $dto->getTitle())->first();
if ($jobTitle instanceof JobTitle) {
$newUser->update(['job_title_id' => $jobTitle->getId()]);
} else {
/** @var JobTitle $newJobTitle */
$newJobTitle = $team->jobs()->create([
'name' => $dto->getTitle(),
'is_selectable' => 0,
]);
$newUser->update(['job_title_id' => $newJobTitle->getId()]);
}
}
if ($newUser->wasRecentlyCreated) {
$this->eventDispatcher->dispatch(new UserCreated($newUser));
}
return $newUser;
} catch (Exception $exception) {
Sentry::captureException($exception);
return null;
}
}
private function isValidPhone(?string $number, string $countryCode): bool
{
if ($number === null) {
return true;
}
$validator = Validator::make(
['phone' => $number],
['phone' => ['phone:INTERNATIONAL,' . $countryCode]]
);
return ! $validator->fails();
}
private function assignRolesFromScim(User $user, CoreUserRequest $dto, Team $team): void
{
$requestedRoles = $dto->getRoles();
if (empty($requestedRoles)) {
if (! $user->roles()->exists()) {
$user->addRole(User::ROLE_ANALYST);
/** @var Role $role */
$role = Role::query()->where(['name' => User::ROLE_ANALYST])->firstOrFail();
$this->roleChangeLogger->log(
activeUser: null,
affected: $user,
role: $role,
type: $user->wasRecentlyCreated
? RoleChangeType::CREATE_USER
: RoleChangeType::ADD_ROLE,
context: RoleChangeContext::SCIM,
);
}
return;
}
$visibleRoles = $this->roleRepository->findAllByTeam($team->getId());
$roleIds = [];
foreach ($requestedRoles as $roleName) {
$role = $visibleRoles->firstWhere('name', $roleName);
if ($role === null) {
continue;
}
$roleIds[] = $role->getId();
}
if (empty($roleIds)) {
return;
}
$validator = Validator::make(
['roles' => $roleIds],
[
'roles' => [
'required',
'array',
'min:1',
new DependentRolesRule($visibleRoles),
],
'roles.*' => [
'required',
'distinct',
'in:' . $visibleRoles->pluck('id')->implode(','),
new ListenerRoleRequiresListenerFeatureRule($team),
],
]
);
if ($validator->fails()) {
return;
}
$roles = $visibleRoles->whereIn('id', $roleIds);
$dto = new UserRolesSyncDTO(
user: $user,
activeUser: null,
roles: $roles,
);
$this->updateUserRolesAction->execute($dto);
}
public function deleteGroup($group): bool
{
$users = $group->members()->get();
foreach ($users as $user) {
$user->update(['group_id' => null]);
}
return $group->delete();
}
public function createNewGroup(CoreGroupRequest $dto, Team $team): ?Group
{
try {
/** @var Group|null $existingGroup */
$existingGroup = $team->groups()->withTrashed()->where('name', $dto->getDisplayName())->first();
if ($existingGroup) {
if ($existingGroup->getAttribute('deleted_at')) {
$existingGroup->restore();
}
return $existingGroup;
}
/** @var Group $newGroup */
$newGroup = $team->groups()->updateOrCreate(
['name' => $dto->getDisplayName()],
[
'slug' => str_replace(' ', '-', strtolower($dto->getDisplayName())),
'user_id' => $team->getOwner()->getId(),
'transcription_model_locale_id' => $team->getTranscriptionModelLocaleId(),
'default_timezone' => $team->getDefaultTimezone(),
'default_language' => $team->getDefaultLanguage(),
'country_code' => $team->getCountryCode(),
'playbook_id' => $team->getDefaultPlaybookId(),
],
);
return $newGroup;
} catch (Exception $exception) {
Sentry::captureException($exception);
return null;
}
}
public function updateGroupAttributesIfChanged(Group $group, CoreGroupRequest $dto, Team $team): Group
{
$actions = [];
if ($group->getName() !== $dto->getDisplayName()) {
$actions[] = $this->returnAction(
Constants::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getDisplayName(),
);
}
foreach ($actions as $action) {
$group = (new GroupPatchOperation($group, $action))->doUpdate();
}
return $group;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny_jupiter
Sync Changes
Hide This Notification
Code changed:
Hide
19
19
17
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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","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":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\SCIM;\n\nuse Exception;\nuse Illuminate\\Contracts\\Events\\Dispatcher;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Component\\Acl\\RoleChangeContext;\nuse Jiminny\\Component\\Acl\\RoleChangeLogger;\nuse Jiminny\\Component\\Acl\\RoleChangeType;\nuse Jiminny\\Component\\SCIM\\Builders\\GroupFilterQueryBuilder;\nuse Jiminny\\Component\\SCIM\\Builders\\UsersFilterQueryBuilder;\nuse Jiminny\\Component\\SCIM\\Mutators\\GroupPatchOperation;\nuse Jiminny\\Component\\SCIM\\Mutators\\UserPatchOperation;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\CoreGroupRequest;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\CoreUserRequest;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\GetGroupByQuery;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\GetUserByQuery;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\PatchGroup;\nuse Jiminny\\Events\\Users\\UserCreated;\nuse Jiminny\\Models\\Group;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\UserService;\nuse Jiminny\\Contracts\\Acl\\RoleRepositoryInterface;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Rules\\DependentRolesRule;\nuse Jiminny\\Rules\\ListenerRoleRequiresListenerFeatureRule;\nuse Jiminny\\Actions\\UpdateUserRolesAction;\nuse Jiminny\\DTO\\Roles\\UserRolesSyncDTO;\nuse Sentry;\n\nclass ScimProvisioning\n{\n public function __construct(\n private readonly Dispatcher $eventDispatcher,\n private readonly UserService $userService,\n private readonly RoleChangeLogger $roleChangeLogger,\n private readonly RoleRepositoryInterface $roleRepository,\n private readonly UpdateUserRolesAction $updateUserRolesAction,\n ) {\n }\n\n public function validateUserIdInput($id, Team $team): bool\n {\n if (! User::isUuid4($id)) {\n return false;\n }\n $user = $this->getUserById($id);\n\n return $user instanceof User && $team->isSame($user->getTeam());\n }\n\n public function updateUserAttributesIfChanged(User $user, CoreUserRequest $dto, Team $team): User\n {\n $actions = [];\n if (! str_starts_with($user->getName(), $dto->getFirstName() . ' ')) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_FIRST_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getFirstName(),\n );\n }\n if (! str_contains($user->getName(), ' ' . $dto->getLastName())) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_LAST_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getLastName(),\n );\n }\n if ((! $user->isStatusActive() && in_array(\n array_get(\n $dto->getRawRequest(),\n Constants::USER_PATCH_ATTR_ACTIVE,\n ),\n [true, 'True', 1],\n true,\n )) ||\n ($user->isStatusActive() && in_array(\n array_get(\n $dto->getRawRequest(),\n Constants::USER_PATCH_ATTR_ACTIVE,\n ),\n [false, 'False', 0],\n true,\n ))\n ) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_ACTIVE,\n Constants::META_OPERATIONS_REPLACE,\n $user->getStatus() === 1 ? 0 : 1,\n );\n }\n if ((! $user->job()->exists() && ! in_array($dto->getTitle(), ['', null])) ||\n (! in_array($dto->getTitle(), ['', null]) && $user->getJobTitle()->name !== $dto->getTitle())\n ) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_TITLE,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getTitle(),\n );\n }\n if ($user->getEmailAddress() !== $dto->getEmailPrimaryWork()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PRIMARY_EMAIL,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getEmailPrimaryWork(),\n );\n }\n if ($user->getSecondaryEmailAddress() !== $dto->getEmailOther()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_SECONDARY_EMAIL,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getEmailOther(),\n );\n }\n if ($user->getAttribute('phone') !== $dto->getPrimaryPhoneNumber()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PHONE_NUMBER,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getPrimaryPhoneNumber() ?? '',\n );\n }\n if ($user->getAttribute('secondary_phone') !== $dto->getSecondaryPhoneNumber()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getSecondaryPhoneNumber() ?? '',\n );\n }\n // no username update support. We rely on email=username in general\n\n foreach ($actions as $action) {\n $user = (new UserPatchOperation($user, $action))->doUpdate();\n }\n\n return $user;\n }\n\n /** @return array{path: string, op: string, value: mixed} */\n private function returnAction(string $path, string $op, mixed $value): array\n {\n return [\n Constants::META_PATH => $path,\n Constants::META_OP => $op,\n Constants::META_VALUE => $value,\n ];\n }\n\n public function getUserById(string $id): ?User\n {\n return User::uuid($id, false)->first();\n }\n\n public function getGroupById(string $id): ?Group\n {\n return Group::uuid($id, false)->first();\n }\n\n public function validateGroupIdInput(string $id, Team $team): bool\n {\n if (! Group::isUuid4($id)) {\n return false;\n }\n\n try {\n /** @var Group|null $group */\n $group = Group::uuid($id);\n } catch (Exception) {\n return false;\n }\n\n return $group instanceof Group && $team->isSame($group->getTeam());\n }\n\n public function patchUserOperation(array $actions, User $user): User\n {\n /**\n * Supported operations\n * on emails, phones, name, title, status except remove primaryEmail\n */\n foreach ($actions as $action) {\n $user = (new UserPatchOperation($user, $action))->doUpdate();\n }\n\n return $user;\n }\n\n public function translateQueryStringsToGroupModelQuery(GetGroupByQuery $dto, Team $team): Builder\n {\n $query = Group::query()\n ->where('team_id', $team->getId());\n\n foreach ($dto->getFilters() as $filter) {\n (new GroupFilterQueryBuilder($filter))->addToQuery($query);\n }\n\n return $query;\n }\n\n public function patchGroupOperation(PatchGroup $dto, Team $team, Group $group): bool\n {\n /**\n * Supported operations\n * - replace group name\n * - add user member of the group\n */\n foreach ($dto->getActions() as $action) {\n $group = (new GroupPatchOperation($group, $action))->doUpdate();\n }\n\n return true;\n }\n\n public function translateQueryStringsToUserModelQuery(GetUserByQuery $dto, Team $team): Builder\n {\n $query = User::query()\n ->where('team_id', $team->getId());\n\n foreach ($dto->getFilters() as $filter) {\n (new UsersFilterQueryBuilder($filter))->addToQuery($query);\n }\n\n return $query;\n }\n\n public function createOrUpdateNewUser(CoreUserRequest $dto, Team $team): ?User\n {\n $phone = $dto->getPrimaryPhoneNumber();\n $secondaryPhone = $dto->getSecondaryPhoneNumber();\n\n if (! $this->isValidPhone($phone, $team->country_code)) {\n $phone = null;\n }\n\n if (! $this->isValidPhone($secondaryPhone, $team->country_code)) {\n $secondaryPhone = null;\n }\n\n $isActive = $dto->getActive();\n\n $userUpdateData = [\n 'name' => $dto->getName(),\n 'secondary_email' => $dto->getEmailOther(),\n 'status' => $isActive ? User::STATUS_ACTIVE : User::STATUS_DEACTIVATED,\n 'language' => $team->default_language,\n 'timezone' => $team->default_timezone,\n 'country_code' => $team->country_code,\n 'conference_slug' => $this->userService->generateSlug($dto->getName()),\n ];\n\n // Do not change phone if it is not provided\n if ($phone !== null) {\n $userUpdateData['phone'] = $phone;\n }\n\n // Do not change secondary phone if it is not provided\n if ($secondaryPhone !== null) {\n $userUpdateData['secondary_phone'] = $secondaryPhone;\n }\n\n try {\n /** @var User $newUser */\n $newUser = $team\n ->users()\n ->whereIn('users.status', [User::STATUS_ACTIVE, User::STATUS_DEACTIVATED])\n ->updateOrCreate(\n ['email' => $dto->getEmailPrimaryWork()],\n $userUpdateData,\n );\n\n $this->assignRolesFromScim($newUser, $dto, $team);\n\n if (! empty($dto->getTitle())) {\n $jobTitle = $team->jobs()->where('name', $dto->getTitle())->first();\n if ($jobTitle instanceof JobTitle) {\n $newUser->update(['job_title_id' => $jobTitle->getId()]);\n } else {\n /** @var JobTitle $newJobTitle */\n $newJobTitle = $team->jobs()->create([\n 'name' => $dto->getTitle(),\n 'is_selectable' => 0,\n ]);\n $newUser->update(['job_title_id' => $newJobTitle->getId()]);\n }\n }\n\n if ($newUser->wasRecentlyCreated) {\n $this->eventDispatcher->dispatch(new UserCreated($newUser));\n }\n\n return $newUser;\n } catch (Exception $exception) {\n Sentry::captureException($exception);\n\n return null;\n }\n }\n\n private function isValidPhone(?string $number, string $countryCode): bool\n {\n if ($number === null) {\n return true;\n }\n\n $validator = Validator::make(\n ['phone' => $number],\n ['phone' => ['phone:INTERNATIONAL,' . $countryCode]]\n );\n\n return ! $validator->fails();\n }\n\n private function assignRolesFromScim(User $user, CoreUserRequest $dto, Team $team): void\n {\n $requestedRoles = $dto->getRoles();\n\n if (empty($requestedRoles)) {\n if (! $user->roles()->exists()) {\n $user->addRole(User::ROLE_ANALYST);\n /** @var Role $role */\n $role = Role::query()->where(['name' => User::ROLE_ANALYST])->firstOrFail();\n\n $this->roleChangeLogger->log(\n activeUser: null,\n affected: $user,\n role: $role,\n type: $user->wasRecentlyCreated\n ? RoleChangeType::CREATE_USER\n : RoleChangeType::ADD_ROLE,\n context: RoleChangeContext::SCIM,\n );\n }\n\n return;\n }\n\n $visibleRoles = $this->roleRepository->findAllByTeam($team->getId());\n\n $roleIds = [];\n foreach ($requestedRoles as $roleName) {\n $role = $visibleRoles->firstWhere('name', $roleName);\n\n if ($role === null) {\n continue;\n }\n\n $roleIds[] = $role->getId();\n }\n\n if (empty($roleIds)) {\n return;\n }\n\n $validator = Validator::make(\n ['roles' => $roleIds],\n [\n 'roles' => [\n 'required',\n 'array',\n 'min:1',\n new DependentRolesRule($visibleRoles),\n ],\n 'roles.*' => [\n 'required',\n 'distinct',\n 'in:' . $visibleRoles->pluck('id')->implode(','),\n new ListenerRoleRequiresListenerFeatureRule($team),\n ],\n ]\n );\n\n if ($validator->fails()) {\n return;\n }\n\n $roles = $visibleRoles->whereIn('id', $roleIds);\n\n $dto = new UserRolesSyncDTO(\n user: $user,\n activeUser: null,\n roles: $roles,\n );\n\n $this->updateUserRolesAction->execute($dto);\n }\n\n public function deleteGroup($group): bool\n {\n $users = $group->members()->get();\n\n foreach ($users as $user) {\n $user->update(['group_id' => null]);\n }\n\n return $group->delete();\n }\n\n public function createNewGroup(CoreGroupRequest $dto, Team $team): ?Group\n {\n try {\n /** @var Group|null $existingGroup */\n $existingGroup = $team->groups()->withTrashed()->where('name', $dto->getDisplayName())->first();\n\n if ($existingGroup) {\n if ($existingGroup->getAttribute('deleted_at')) {\n $existingGroup->restore();\n }\n\n return $existingGroup;\n }\n\n /** @var Group $newGroup */\n $newGroup = $team->groups()->updateOrCreate(\n ['name' => $dto->getDisplayName()],\n [\n 'slug' => str_replace(' ', '-', strtolower($dto->getDisplayName())),\n 'user_id' => $team->getOwner()->getId(),\n 'transcription_model_locale_id' => $team->getTranscriptionModelLocaleId(),\n 'default_timezone' => $team->getDefaultTimezone(),\n 'default_language' => $team->getDefaultLanguage(),\n 'country_code' => $team->getCountryCode(),\n 'playbook_id' => $team->getDefaultPlaybookId(),\n ],\n );\n\n return $newGroup;\n } catch (Exception $exception) {\n Sentry::captureException($exception);\n\n return null;\n }\n }\n\n public function updateGroupAttributesIfChanged(Group $group, CoreGroupRequest $dto, Team $team): Group\n {\n $actions = [];\n\n if ($group->getName() !== $dto->getDisplayName()) {\n $actions[] = $this->returnAction(\n Constants::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getDisplayName(),\n );\n }\n\n foreach ($actions as $action) {\n $group = (new GroupPatchOperation($group, $action))->doUpdate();\n }\n\n return $group;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\SCIM;\n\nuse Exception;\nuse Illuminate\\Contracts\\Events\\Dispatcher;\nuse Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Component\\Acl\\RoleChangeContext;\nuse Jiminny\\Component\\Acl\\RoleChangeLogger;\nuse Jiminny\\Component\\Acl\\RoleChangeType;\nuse Jiminny\\Component\\SCIM\\Builders\\GroupFilterQueryBuilder;\nuse Jiminny\\Component\\SCIM\\Builders\\UsersFilterQueryBuilder;\nuse Jiminny\\Component\\SCIM\\Mutators\\GroupPatchOperation;\nuse Jiminny\\Component\\SCIM\\Mutators\\UserPatchOperation;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\CoreGroupRequest;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\CoreUserRequest;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\GetGroupByQuery;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\GetUserByQuery;\nuse Jiminny\\DTO\\SCIM\\AAD\\Request\\PatchGroup;\nuse Jiminny\\Events\\Users\\UserCreated;\nuse Jiminny\\Models\\Group;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\UserService;\nuse Jiminny\\Contracts\\Acl\\RoleRepositoryInterface;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Rules\\DependentRolesRule;\nuse Jiminny\\Rules\\ListenerRoleRequiresListenerFeatureRule;\nuse Jiminny\\Actions\\UpdateUserRolesAction;\nuse Jiminny\\DTO\\Roles\\UserRolesSyncDTO;\nuse Sentry;\n\nclass ScimProvisioning\n{\n public function __construct(\n private readonly Dispatcher $eventDispatcher,\n private readonly UserService $userService,\n private readonly RoleChangeLogger $roleChangeLogger,\n private readonly RoleRepositoryInterface $roleRepository,\n private readonly UpdateUserRolesAction $updateUserRolesAction,\n ) {\n }\n\n public function validateUserIdInput($id, Team $team): bool\n {\n if (! User::isUuid4($id)) {\n return false;\n }\n $user = $this->getUserById($id);\n\n return $user instanceof User && $team->isSame($user->getTeam());\n }\n\n public function updateUserAttributesIfChanged(User $user, CoreUserRequest $dto, Team $team): User\n {\n $actions = [];\n if (! str_starts_with($user->getName(), $dto->getFirstName() . ' ')) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_FIRST_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getFirstName(),\n );\n }\n if (! str_contains($user->getName(), ' ' . $dto->getLastName())) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_LAST_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getLastName(),\n );\n }\n if ((! $user->isStatusActive() && in_array(\n array_get(\n $dto->getRawRequest(),\n Constants::USER_PATCH_ATTR_ACTIVE,\n ),\n [true, 'True', 1],\n true,\n )) ||\n ($user->isStatusActive() && in_array(\n array_get(\n $dto->getRawRequest(),\n Constants::USER_PATCH_ATTR_ACTIVE,\n ),\n [false, 'False', 0],\n true,\n ))\n ) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_ACTIVE,\n Constants::META_OPERATIONS_REPLACE,\n $user->getStatus() === 1 ? 0 : 1,\n );\n }\n if ((! $user->job()->exists() && ! in_array($dto->getTitle(), ['', null])) ||\n (! in_array($dto->getTitle(), ['', null]) && $user->getJobTitle()->name !== $dto->getTitle())\n ) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_TITLE,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getTitle(),\n );\n }\n if ($user->getEmailAddress() !== $dto->getEmailPrimaryWork()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PRIMARY_EMAIL,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getEmailPrimaryWork(),\n );\n }\n if ($user->getSecondaryEmailAddress() !== $dto->getEmailOther()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_SECONDARY_EMAIL,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getEmailOther(),\n );\n }\n if ($user->getAttribute('phone') !== $dto->getPrimaryPhoneNumber()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PHONE_NUMBER,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getPrimaryPhoneNumber() ?? '',\n );\n }\n if ($user->getAttribute('secondary_phone') !== $dto->getSecondaryPhoneNumber()) {\n $actions[] = $this->returnAction(\n Constants::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getSecondaryPhoneNumber() ?? '',\n );\n }\n // no username update support. We rely on email=username in general\n\n foreach ($actions as $action) {\n $user = (new UserPatchOperation($user, $action))->doUpdate();\n }\n\n return $user;\n }\n\n /** @return array{path: string, op: string, value: mixed} */\n private function returnAction(string $path, string $op, mixed $value): array\n {\n return [\n Constants::META_PATH => $path,\n Constants::META_OP => $op,\n Constants::META_VALUE => $value,\n ];\n }\n\n public function getUserById(string $id): ?User\n {\n return User::uuid($id, false)->first();\n }\n\n public function getGroupById(string $id): ?Group\n {\n return Group::uuid($id, false)->first();\n }\n\n public function validateGroupIdInput(string $id, Team $team): bool\n {\n if (! Group::isUuid4($id)) {\n return false;\n }\n\n try {\n /** @var Group|null $group */\n $group = Group::uuid($id);\n } catch (Exception) {\n return false;\n }\n\n return $group instanceof Group && $team->isSame($group->getTeam());\n }\n\n public function patchUserOperation(array $actions, User $user): User\n {\n /**\n * Supported operations\n * on emails, phones, name, title, status except remove primaryEmail\n */\n foreach ($actions as $action) {\n $user = (new UserPatchOperation($user, $action))->doUpdate();\n }\n\n return $user;\n }\n\n public function translateQueryStringsToGroupModelQuery(GetGroupByQuery $dto, Team $team): Builder\n {\n $query = Group::query()\n ->where('team_id', $team->getId());\n\n foreach ($dto->getFilters() as $filter) {\n (new GroupFilterQueryBuilder($filter))->addToQuery($query);\n }\n\n return $query;\n }\n\n public function patchGroupOperation(PatchGroup $dto, Team $team, Group $group): bool\n {\n /**\n * Supported operations\n * - replace group name\n * - add user member of the group\n */\n foreach ($dto->getActions() as $action) {\n $group = (new GroupPatchOperation($group, $action))->doUpdate();\n }\n\n return true;\n }\n\n public function translateQueryStringsToUserModelQuery(GetUserByQuery $dto, Team $team): Builder\n {\n $query = User::query()\n ->where('team_id', $team->getId());\n\n foreach ($dto->getFilters() as $filter) {\n (new UsersFilterQueryBuilder($filter))->addToQuery($query);\n }\n\n return $query;\n }\n\n public function createOrUpdateNewUser(CoreUserRequest $dto, Team $team): ?User\n {\n $phone = $dto->getPrimaryPhoneNumber();\n $secondaryPhone = $dto->getSecondaryPhoneNumber();\n\n if (! $this->isValidPhone($phone, $team->country_code)) {\n $phone = null;\n }\n\n if (! $this->isValidPhone($secondaryPhone, $team->country_code)) {\n $secondaryPhone = null;\n }\n\n $isActive = $dto->getActive();\n\n $userUpdateData = [\n 'name' => $dto->getName(),\n 'secondary_email' => $dto->getEmailOther(),\n 'status' => $isActive ? User::STATUS_ACTIVE : User::STATUS_DEACTIVATED,\n 'language' => $team->default_language,\n 'timezone' => $team->default_timezone,\n 'country_code' => $team->country_code,\n 'conference_slug' => $this->userService->generateSlug($dto->getName()),\n ];\n\n // Do not change phone if it is not provided\n if ($phone !== null) {\n $userUpdateData['phone'] = $phone;\n }\n\n // Do not change secondary phone if it is not provided\n if ($secondaryPhone !== null) {\n $userUpdateData['secondary_phone'] = $secondaryPhone;\n }\n\n try {\n /** @var User $newUser */\n $newUser = $team\n ->users()\n ->whereIn('users.status', [User::STATUS_ACTIVE, User::STATUS_DEACTIVATED])\n ->updateOrCreate(\n ['email' => $dto->getEmailPrimaryWork()],\n $userUpdateData,\n );\n\n $this->assignRolesFromScim($newUser, $dto, $team);\n\n if (! empty($dto->getTitle())) {\n $jobTitle = $team->jobs()->where('name', $dto->getTitle())->first();\n if ($jobTitle instanceof JobTitle) {\n $newUser->update(['job_title_id' => $jobTitle->getId()]);\n } else {\n /** @var JobTitle $newJobTitle */\n $newJobTitle = $team->jobs()->create([\n 'name' => $dto->getTitle(),\n 'is_selectable' => 0,\n ]);\n $newUser->update(['job_title_id' => $newJobTitle->getId()]);\n }\n }\n\n if ($newUser->wasRecentlyCreated) {\n $this->eventDispatcher->dispatch(new UserCreated($newUser));\n }\n\n return $newUser;\n } catch (Exception $exception) {\n Sentry::captureException($exception);\n\n return null;\n }\n }\n\n private function isValidPhone(?string $number, string $countryCode): bool\n {\n if ($number === null) {\n return true;\n }\n\n $validator = Validator::make(\n ['phone' => $number],\n ['phone' => ['phone:INTERNATIONAL,' . $countryCode]]\n );\n\n return ! $validator->fails();\n }\n\n private function assignRolesFromScim(User $user, CoreUserRequest $dto, Team $team): void\n {\n $requestedRoles = $dto->getRoles();\n\n if (empty($requestedRoles)) {\n if (! $user->roles()->exists()) {\n $user->addRole(User::ROLE_ANALYST);\n /** @var Role $role */\n $role = Role::query()->where(['name' => User::ROLE_ANALYST])->firstOrFail();\n\n $this->roleChangeLogger->log(\n activeUser: null,\n affected: $user,\n role: $role,\n type: $user->wasRecentlyCreated\n ? RoleChangeType::CREATE_USER\n : RoleChangeType::ADD_ROLE,\n context: RoleChangeContext::SCIM,\n );\n }\n\n return;\n }\n\n $visibleRoles = $this->roleRepository->findAllByTeam($team->getId());\n\n $roleIds = [];\n foreach ($requestedRoles as $roleName) {\n $role = $visibleRoles->firstWhere('name', $roleName);\n\n if ($role === null) {\n continue;\n }\n\n $roleIds[] = $role->getId();\n }\n\n if (empty($roleIds)) {\n return;\n }\n\n $validator = Validator::make(\n ['roles' => $roleIds],\n [\n 'roles' => [\n 'required',\n 'array',\n 'min:1',\n new DependentRolesRule($visibleRoles),\n ],\n 'roles.*' => [\n 'required',\n 'distinct',\n 'in:' . $visibleRoles->pluck('id')->implode(','),\n new ListenerRoleRequiresListenerFeatureRule($team),\n ],\n ]\n );\n\n if ($validator->fails()) {\n return;\n }\n\n $roles = $visibleRoles->whereIn('id', $roleIds);\n\n $dto = new UserRolesSyncDTO(\n user: $user,\n activeUser: null,\n roles: $roles,\n );\n\n $this->updateUserRolesAction->execute($dto);\n }\n\n public function deleteGroup($group): bool\n {\n $users = $group->members()->get();\n\n foreach ($users as $user) {\n $user->update(['group_id' => null]);\n }\n\n return $group->delete();\n }\n\n public function createNewGroup(CoreGroupRequest $dto, Team $team): ?Group\n {\n try {\n /** @var Group|null $existingGroup */\n $existingGroup = $team->groups()->withTrashed()->where('name', $dto->getDisplayName())->first();\n\n if ($existingGroup) {\n if ($existingGroup->getAttribute('deleted_at')) {\n $existingGroup->restore();\n }\n\n return $existingGroup;\n }\n\n /** @var Group $newGroup */\n $newGroup = $team->groups()->updateOrCreate(\n ['name' => $dto->getDisplayName()],\n [\n 'slug' => str_replace(' ', '-', strtolower($dto->getDisplayName())),\n 'user_id' => $team->getOwner()->getId(),\n 'transcription_model_locale_id' => $team->getTranscriptionModelLocaleId(),\n 'default_timezone' => $team->getDefaultTimezone(),\n 'default_language' => $team->getDefaultLanguage(),\n 'country_code' => $team->getCountryCode(),\n 'playbook_id' => $team->getDefaultPlaybookId(),\n ],\n );\n\n return $newGroup;\n } catch (Exception $exception) {\n Sentry::captureException($exception);\n\n return null;\n }\n }\n\n public function updateGroupAttributesIfChanged(Group $group, CoreGroupRequest $dto, Team $team): Group\n {\n $actions = [];\n\n if ($group->getName() !== $dto->getDisplayName()) {\n $actions[] = $this->returnAction(\n Constants::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME,\n Constants::META_OPERATIONS_REPLACE,\n $dto->getDisplayName(),\n );\n }\n\n foreach ($actions as $action) {\n $group = (new GroupPatchOperation($group, $action))->doUpdate();\n }\n\n return $group;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny_jupiter","depth":4,"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":"19","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"19","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}]...
|
2220240040950549119
|
5527256463689486230
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Jiminny\Component\SCIM;
use Exception;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Validator;
use Jiminny\Component\Acl\RoleChangeContext;
use Jiminny\Component\Acl\RoleChangeLogger;
use Jiminny\Component\Acl\RoleChangeType;
use Jiminny\Component\SCIM\Builders\GroupFilterQueryBuilder;
use Jiminny\Component\SCIM\Builders\UsersFilterQueryBuilder;
use Jiminny\Component\SCIM\Mutators\GroupPatchOperation;
use Jiminny\Component\SCIM\Mutators\UserPatchOperation;
use Jiminny\DTO\SCIM\AAD\Request\CoreGroupRequest;
use Jiminny\DTO\SCIM\AAD\Request\CoreUserRequest;
use Jiminny\DTO\SCIM\AAD\Request\GetGroupByQuery;
use Jiminny\DTO\SCIM\AAD\Request\GetUserByQuery;
use Jiminny\DTO\SCIM\AAD\Request\PatchGroup;
use Jiminny\Events\Users\UserCreated;
use Jiminny\Models\Group;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Role;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Services\UserService;
use Jiminny\Contracts\Acl\RoleRepositoryInterface;
use Illuminate\Support\Facades\Validator;
use Jiminny\Rules\DependentRolesRule;
use Jiminny\Rules\ListenerRoleRequiresListenerFeatureRule;
use Jiminny\Actions\UpdateUserRolesAction;
use Jiminny\DTO\Roles\UserRolesSyncDTO;
use Sentry;
class ScimProvisioning
{
public function __construct(
private readonly Dispatcher $eventDispatcher,
private readonly UserService $userService,
private readonly RoleChangeLogger $roleChangeLogger,
private readonly RoleRepositoryInterface $roleRepository,
private readonly UpdateUserRolesAction $updateUserRolesAction,
) {
}
public function validateUserIdInput($id, Team $team): bool
{
if (! User::isUuid4($id)) {
return false;
}
$user = $this->getUserById($id);
return $user instanceof User && $team->isSame($user->getTeam());
}
public function updateUserAttributesIfChanged(User $user, CoreUserRequest $dto, Team $team): User
{
$actions = [];
if (! str_starts_with($user->getName(), $dto->getFirstName() . ' ')) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_FIRST_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getFirstName(),
);
}
if (! str_contains($user->getName(), ' ' . $dto->getLastName())) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_LAST_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getLastName(),
);
}
if ((! $user->isStatusActive() && in_array(
array_get(
$dto->getRawRequest(),
Constants::USER_PATCH_ATTR_ACTIVE,
),
[true, 'True', 1],
true,
)) ||
($user->isStatusActive() && in_array(
array_get(
$dto->getRawRequest(),
Constants::USER_PATCH_ATTR_ACTIVE,
),
[false, 'False', 0],
true,
))
) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_ACTIVE,
Constants::META_OPERATIONS_REPLACE,
$user->getStatus() === 1 ? 0 : 1,
);
}
if ((! $user->job()->exists() && ! in_array($dto->getTitle(), ['', null])) ||
(! in_array($dto->getTitle(), ['', null]) && $user->getJobTitle()->name !== $dto->getTitle())
) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_TITLE,
Constants::META_OPERATIONS_REPLACE,
$dto->getTitle(),
);
}
if ($user->getEmailAddress() !== $dto->getEmailPrimaryWork()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PRIMARY_EMAIL,
Constants::META_OPERATIONS_REPLACE,
$dto->getEmailPrimaryWork(),
);
}
if ($user->getSecondaryEmailAddress() !== $dto->getEmailOther()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_SECONDARY_EMAIL,
Constants::META_OPERATIONS_REPLACE,
$dto->getEmailOther(),
);
}
if ($user->getAttribute('phone') !== $dto->getPrimaryPhoneNumber()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PHONE_NUMBER,
Constants::META_OPERATIONS_REPLACE,
$dto->getPrimaryPhoneNumber() ?? '',
);
}
if ($user->getAttribute('secondary_phone') !== $dto->getSecondaryPhoneNumber()) {
$actions[] = $this->returnAction(
Constants::USER_PATCH_ATTR_PHONE_NUMBER_MOBILE,
Constants::META_OPERATIONS_REPLACE,
$dto->getSecondaryPhoneNumber() ?? '',
);
}
// no username update support. We rely on email=username in general
foreach ($actions as $action) {
$user = (new UserPatchOperation($user, $action))->doUpdate();
}
return $user;
}
/** @return array{path: string, op: string, value: mixed} */
private function returnAction(string $path, string $op, mixed $value): array
{
return [
Constants::META_PATH => $path,
Constants::META_OP => $op,
Constants::META_VALUE => $value,
];
}
public function getUserById(string $id): ?User
{
return User::uuid($id, false)->first();
}
public function getGroupById(string $id): ?Group
{
return Group::uuid($id, false)->first();
}
public function validateGroupIdInput(string $id, Team $team): bool
{
if (! Group::isUuid4($id)) {
return false;
}
try {
/** @var Group|null $group */
$group = Group::uuid($id);
} catch (Exception) {
return false;
}
return $group instanceof Group && $team->isSame($group->getTeam());
}
public function patchUserOperation(array $actions, User $user): User
{
/**
* Supported operations
* on emails, phones, name, title, status except remove primaryEmail
*/
foreach ($actions as $action) {
$user = (new UserPatchOperation($user, $action))->doUpdate();
}
return $user;
}
public function translateQueryStringsToGroupModelQuery(GetGroupByQuery $dto, Team $team): Builder
{
$query = Group::query()
->where('team_id', $team->getId());
foreach ($dto->getFilters() as $filter) {
(new GroupFilterQueryBuilder($filter))->addToQuery($query);
}
return $query;
}
public function patchGroupOperation(PatchGroup $dto, Team $team, Group $group): bool
{
/**
* Supported operations
* - replace group name
* - add user member of the group
*/
foreach ($dto->getActions() as $action) {
$group = (new GroupPatchOperation($group, $action))->doUpdate();
}
return true;
}
public function translateQueryStringsToUserModelQuery(GetUserByQuery $dto, Team $team): Builder
{
$query = User::query()
->where('team_id', $team->getId());
foreach ($dto->getFilters() as $filter) {
(new UsersFilterQueryBuilder($filter))->addToQuery($query);
}
return $query;
}
public function createOrUpdateNewUser(CoreUserRequest $dto, Team $team): ?User
{
$phone = $dto->getPrimaryPhoneNumber();
$secondaryPhone = $dto->getSecondaryPhoneNumber();
if (! $this->isValidPhone($phone, $team->country_code)) {
$phone = null;
}
if (! $this->isValidPhone($secondaryPhone, $team->country_code)) {
$secondaryPhone = null;
}
$isActive = $dto->getActive();
$userUpdateData = [
'name' => $dto->getName(),
'secondary_email' => $dto->getEmailOther(),
'status' => $isActive ? User::STATUS_ACTIVE : User::STATUS_DEACTIVATED,
'language' => $team->default_language,
'timezone' => $team->default_timezone,
'country_code' => $team->country_code,
'conference_slug' => $this->userService->generateSlug($dto->getName()),
];
// Do not change phone if it is not provided
if ($phone !== null) {
$userUpdateData['phone'] = $phone;
}
// Do not change secondary phone if it is not provided
if ($secondaryPhone !== null) {
$userUpdateData['secondary_phone'] = $secondaryPhone;
}
try {
/** @var User $newUser */
$newUser = $team
->users()
->whereIn('users.status', [User::STATUS_ACTIVE, User::STATUS_DEACTIVATED])
->updateOrCreate(
['email' => $dto->getEmailPrimaryWork()],
$userUpdateData,
);
$this->assignRolesFromScim($newUser, $dto, $team);
if (! empty($dto->getTitle())) {
$jobTitle = $team->jobs()->where('name', $dto->getTitle())->first();
if ($jobTitle instanceof JobTitle) {
$newUser->update(['job_title_id' => $jobTitle->getId()]);
} else {
/** @var JobTitle $newJobTitle */
$newJobTitle = $team->jobs()->create([
'name' => $dto->getTitle(),
'is_selectable' => 0,
]);
$newUser->update(['job_title_id' => $newJobTitle->getId()]);
}
}
if ($newUser->wasRecentlyCreated) {
$this->eventDispatcher->dispatch(new UserCreated($newUser));
}
return $newUser;
} catch (Exception $exception) {
Sentry::captureException($exception);
return null;
}
}
private function isValidPhone(?string $number, string $countryCode): bool
{
if ($number === null) {
return true;
}
$validator = Validator::make(
['phone' => $number],
['phone' => ['phone:INTERNATIONAL,' . $countryCode]]
);
return ! $validator->fails();
}
private function assignRolesFromScim(User $user, CoreUserRequest $dto, Team $team): void
{
$requestedRoles = $dto->getRoles();
if (empty($requestedRoles)) {
if (! $user->roles()->exists()) {
$user->addRole(User::ROLE_ANALYST);
/** @var Role $role */
$role = Role::query()->where(['name' => User::ROLE_ANALYST])->firstOrFail();
$this->roleChangeLogger->log(
activeUser: null,
affected: $user,
role: $role,
type: $user->wasRecentlyCreated
? RoleChangeType::CREATE_USER
: RoleChangeType::ADD_ROLE,
context: RoleChangeContext::SCIM,
);
}
return;
}
$visibleRoles = $this->roleRepository->findAllByTeam($team->getId());
$roleIds = [];
foreach ($requestedRoles as $roleName) {
$role = $visibleRoles->firstWhere('name', $roleName);
if ($role === null) {
continue;
}
$roleIds[] = $role->getId();
}
if (empty($roleIds)) {
return;
}
$validator = Validator::make(
['roles' => $roleIds],
[
'roles' => [
'required',
'array',
'min:1',
new DependentRolesRule($visibleRoles),
],
'roles.*' => [
'required',
'distinct',
'in:' . $visibleRoles->pluck('id')->implode(','),
new ListenerRoleRequiresListenerFeatureRule($team),
],
]
);
if ($validator->fails()) {
return;
}
$roles = $visibleRoles->whereIn('id', $roleIds);
$dto = new UserRolesSyncDTO(
user: $user,
activeUser: null,
roles: $roles,
);
$this->updateUserRolesAction->execute($dto);
}
public function deleteGroup($group): bool
{
$users = $group->members()->get();
foreach ($users as $user) {
$user->update(['group_id' => null]);
}
return $group->delete();
}
public function createNewGroup(CoreGroupRequest $dto, Team $team): ?Group
{
try {
/** @var Group|null $existingGroup */
$existingGroup = $team->groups()->withTrashed()->where('name', $dto->getDisplayName())->first();
if ($existingGroup) {
if ($existingGroup->getAttribute('deleted_at')) {
$existingGroup->restore();
}
return $existingGroup;
}
/** @var Group $newGroup */
$newGroup = $team->groups()->updateOrCreate(
['name' => $dto->getDisplayName()],
[
'slug' => str_replace(' ', '-', strtolower($dto->getDisplayName())),
'user_id' => $team->getOwner()->getId(),
'transcription_model_locale_id' => $team->getTranscriptionModelLocaleId(),
'default_timezone' => $team->getDefaultTimezone(),
'default_language' => $team->getDefaultLanguage(),
'country_code' => $team->getCountryCode(),
'playbook_id' => $team->getDefaultPlaybookId(),
],
);
return $newGroup;
} catch (Exception $exception) {
Sentry::captureException($exception);
return null;
}
}
public function updateGroupAttributesIfChanged(Group $group, CoreGroupRequest $dto, Team $team): Group
{
$actions = [];
if ($group->getName() !== $dto->getDisplayName()) {
$actions[] = $this->returnAction(
Constants::GROUP_PATCH_ATTRIBUTE_DISPLAY_NAME,
Constants::META_OPERATIONS_REPLACE,
$dto->getDisplayName(),
);
}
foreach ($actions as $action) {
$group = (new GroupPatchOperation($group, $action))->doUpdate();
}
return $group;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny_jupiter
Sync Changes
Hide This Notification
Code changed:
Hide
19
19
17
Previous Highlighted Error
Next Highlighted Error...
|
53094
|
NULL
|
NULL
|
NULL
|
|
53097
|
1861
|
8
|
2026-05-18T11:37:08.695644+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104228695_m2.jpg...
|
PhpStorm
|
faVsco.js – ListenerRoleCannotHaveAdminOrManagerPe faVsco.js – ListenerRoleCannotHaveAdminOrManagerPermissionRule.php...
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorWindowmelp PhostormVIewINavicarecodeLaravelKeractorWindowmelpFV faVsco.js°9 master kroledeyC ActivityController.ong© SoftPhoneManager.php>@ Buildersv D Mutatorsv D Attributes>D Groupv D UserC) CoreUserRequest.pnpconstants.onp© ScimProvisioning.phpC) ACuiveAttr.ono© FirstNameAttr.php(C) LastNameAttr.php© PhoneNumberPrimaryAttr.phpnamespace Jiminny RuleS:c) PhoneNumberSecondarvAttr.on© PrimaryEmailAttr.php(C) RoleAttr.ohp© SecondarvEmailAttr.ohnFinal readonlv class ListenerRolecannotHaveAdmin0rManagerPermissionPule imolements ValidationRuleC) TiitleAttrono(C) UsernameAttr.ohoC GrounMutator.ohoC UserMutator.php© GroupPatchOperation.php© UserPatchOperation.php© Constants.php© Sciminterface.phppublic function __construct(/** @var Collection<Role> */private Collection Sroles,01...}scmrrovisionine.onoM Seeder* @param int[] $value* @param Closure(string): PotentiallyTranslatedString $failU sentypublic function validate(string Sattribute, mixed Svalue, Closure Sfail): void{...}W setuingsSidekick0 Teaminsights0 TimeMemoryMapper*1ofAedite 1./ Accept File *~ X Reject File 0 2 €+ 4 of 4 files →Local ChangesLOg x• Chances 8 tlles, uodatino...→ E Side-by-side viewer= env.local apo@cb4ebf0c config/logging.php@ Constants.php app/Component/SCiMl(C) CoreUserRequest.oho aoo/DTO/SCIMIAAD/[EMAIL] app/Console/CommaIdnivent =s"Level → env "Lub Level', "Into'),php loadina,oho confid'path' => storage_path('logs/Laravel.log').© ScimProvisioning.php app/Component/SCIM@) SoftPhoneManager.php app/Component/Twilio/Conference/ConferenceManag(C) TeytMessaainaService.nhn ann/Services/TelenhonviUnversioned Files 10 files= .env.nikilocal appenv.other ann(8 CanAcceccAiDonorteTact nhn toctell Init/Doliciod© CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Rf fovicon icn nubli.=ids.txt appTa raw sal query.sal app© RoleAttr.php app/Component/SCIM/Mutators/Attributes/User© SimulateWebhooksCommand.php app/Console/Commands/Crm/HubspotMWCOLIOOK CI TEDING IMDI CAACNITATIONI md onrWorkcnace accociated with hranch 'macter' hac heon rectored /l Pollback II Confiaure (17 minutes aao)=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X Tx: Auto vere cri couT LuuraqionE Tanu UEULaVUUUK LE wO:select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team.id = t.idWHERE r.frequency=and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;|select * from activity searches where id = 10932select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55select * from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985071select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43) = uuid;select * from teams:• suppont Dally • In 23 m100% 2• Mon 18 May 14:37:08AskJiminnyReportActivityServiceTest v& console [PROD]# console leuyCascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..Go jiminny_jupiterThought for 2s019 A19 V17 ^ VScimProvisioning.phpThoughts• ScimProvisioning.phpThought for 1s›• ScimProvisioning.php+1-15ScimProvisionina.phpThouaht for 1s )o10 tasks done• Update ScimProvisioning to handle roles during user creationAdd validation for SCIM role accianment (listener + admin/manader rectriction)Thaucht for 1cAsk anvthina (%4L)+ @code SWE-1.6Reject allAccept all0901ditterenceCurrent vercion'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').W Windsurf Toams 20-1UTF.8io 4 spaces...
|
NULL
|
-5079952164880233096
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorWindowmelp PhostormVIewINavicarecodeLaravelKeractorWindowmelpFV faVsco.js°9 master kroledeyC ActivityController.ong© SoftPhoneManager.php>@ Buildersv D Mutatorsv D Attributes>D Groupv D UserC) CoreUserRequest.pnpconstants.onp© ScimProvisioning.phpC) ACuiveAttr.ono© FirstNameAttr.php(C) LastNameAttr.php© PhoneNumberPrimaryAttr.phpnamespace Jiminny RuleS:c) PhoneNumberSecondarvAttr.on© PrimaryEmailAttr.php(C) RoleAttr.ohp© SecondarvEmailAttr.ohnFinal readonlv class ListenerRolecannotHaveAdmin0rManagerPermissionPule imolements ValidationRuleC) TiitleAttrono(C) UsernameAttr.ohoC GrounMutator.ohoC UserMutator.php© GroupPatchOperation.php© UserPatchOperation.php© Constants.php© Sciminterface.phppublic function __construct(/** @var Collection<Role> */private Collection Sroles,01...}scmrrovisionine.onoM Seeder* @param int[] $value* @param Closure(string): PotentiallyTranslatedString $failU sentypublic function validate(string Sattribute, mixed Svalue, Closure Sfail): void{...}W setuingsSidekick0 Teaminsights0 TimeMemoryMapper*1ofAedite 1./ Accept File *~ X Reject File 0 2 €+ 4 of 4 files →Local ChangesLOg x• Chances 8 tlles, uodatino...→ E Side-by-side viewer= env.local apo@cb4ebf0c config/logging.php@ Constants.php app/Component/SCiMl(C) CoreUserRequest.oho aoo/DTO/SCIMIAAD/[EMAIL] app/Console/CommaIdnivent =s"Level → env "Lub Level', "Into'),php loadina,oho confid'path' => storage_path('logs/Laravel.log').© ScimProvisioning.php app/Component/SCIM@) SoftPhoneManager.php app/Component/Twilio/Conference/ConferenceManag(C) TeytMessaainaService.nhn ann/Services/TelenhonviUnversioned Files 10 files= .env.nikilocal appenv.other ann(8 CanAcceccAiDonorteTact nhn toctell Init/Doliciod© CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Rf fovicon icn nubli.=ids.txt appTa raw sal query.sal app© RoleAttr.php app/Component/SCIM/Mutators/Attributes/User© SimulateWebhooksCommand.php app/Console/Commands/Crm/HubspotMWCOLIOOK CI TEDING IMDI CAACNITATIONI md onrWorkcnace accociated with hranch 'macter' hac heon rectored /l Pollback II Confiaure (17 minutes aao)=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X Tx: Auto vere cri couT LuuraqionE Tanu UEULaVUUUK LE wO:select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team.id = t.idWHERE r.frequency=and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;|select * from activity searches where id = 10932select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55select * from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985071select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43) = uuid;select * from teams:• suppont Dally • In 23 m100% 2• Mon 18 May 14:37:08AskJiminnyReportActivityServiceTest v& console [PROD]# console leuyCascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..Go jiminny_jupiterThought for 2s019 A19 V17 ^ VScimProvisioning.phpThoughts• ScimProvisioning.phpThought for 1s›• ScimProvisioning.php+1-15ScimProvisionina.phpThouaht for 1s )o10 tasks done• Update ScimProvisioning to handle roles during user creationAdd validation for SCIM role accianment (listener + admin/manader rectriction)Thaucht for 1cAsk anvthina (%4L)+ @code SWE-1.6Reject allAccept all0901ditterenceCurrent vercion'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').W Windsurf Toams 20-1UTF.8io 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53098
|
1860
|
7
|
2026-05-18T11:37:27.971546+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104247971_m1.jpg...
|
PhpStorm
|
faVsco.js – RoleAttr.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","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}]...
|
-4235983745889776938
|
-8204421443435123770
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴2X t1asticsearchDOCKER (docker-compose)to Error: No Livingconnections1 {"type" : "log""@timestamp":"2026-05-18T11:37:19Z"asticsearch","tags" : ["warning", "monitoring"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "monitoring"], "pid" :7,{"type" : "log""licensing"],asticsearch*2026-05-18T11:37:19Z": ["warning: "No living connections"}"2026-05-18T11:37:19Z""tags": ["warning"informationcould not be obtained from Elconnectionsticsearch"{"type" : "log""@timestamp":"2026-05-18T11:37:21Z", "tags" : ["error""pid" :7, "message" :"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch: 9200*}kibana{"type" : "log""@timestamp" : "2026-05-18T11:37:21Z" , "tags" : ["error", "elasticsearch","data"],"pid":7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}{"type" : "log""@timestamp": "2026-05-18T11:37:22Z","tags" : ["warning", "elasticsearch","data"], "pid" :7,"message":"Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:37:22Z" , "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message": "No livingconnections"}kibanains"1 {"type" : "log""@timestamp":"2026-05-18T11:37:22Z""tags" : ["error", "taskManager", "taskManager"], "pid" :7, "message" : "Failed to pollfor work: Error: NoLivingconnections"}docker_1amp_12026-05-18 11:37:12 Running ['artisan' meeting-bot:schedule-bot] .. 1US DONEdocker_lamp_1fd/1'1 '/usr/local/bin/php' 'artisan' meeting-bot: schedule-bot > */proc/1/2>&1kibana1 {"type": "log""@timestamp" : "2026-05-18T11:37:24Z", "tags" : ["error""elasticsearch","data"],"pid":7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch: 9200"}kibana1 {"type" : "log""@timestamp": "2026-05-18T11:37:25Z""tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:37:25Z", "tags" : ["warning", "elasticsearch", "data"],"pid":7,"message": "No livingconnections"}kibana1 {"type" : "log"!ins","@timestamp": "2026-05-18T11:37:25Z""tags" : ["error","plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:37:26Z", "tags" : ["error", "elasticsearch", "data"],"pid" :7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}View in Docker Desktop• View ConfigEnable WatchHomeDMsActivityFilesLaterMore-Jiminny ...Metrorrtence# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka StoyanovaStoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov ". Mario Georgiev. Nikolay IvanovLo James Graham *8 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiE. Lukas Kovalik y...AppsJira CloudToastSupport Daily • in 23 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas@ Files• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:37:276 0+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53099
|
1861
|
9
|
2026-05-18T11:37:38.906270+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104258906_m2.jpg...
|
PhpStorm
|
faVsco.js – ScimProvisioning.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js°9 master kProiect© SoftPhoneManager.php© PhoneNumberSecondaryAttr.phC CoreUserRequest.phpscimProvistoning.onpx© PrimaryEmailAttr.php© TextMessagingService.php© RoleAttr.php© SecondaryEmailAttr.phpuse Jiminny DTO\SCIM\AAD \Request \GetUserByQuery:© TitleAttr.phpc usernameAuur.onouse Jiminny DTO\SCIM\AAD \Request \PatchGroup;use Jiminny \Events\Users\UserCreated;© GroupMutator.phpuse Jiminny Models \Group;C UserMutator.phpuse Jiminny Models\JobTitle;c) GroupPatchOperation.phpuse Jiminny Models kole.C) UserPatchOperation.phpuse Jiminny Modets rean,c) constants.ohpuse Jiminny Modeusluser@ Sciminterface.phpuse Jiminny services userservice.c) ScimProvisionina.onouse szninny concracts Act kolekepostcoryencerraceSeedenuse Lulumanare supporc racades valzdacor.Sentryuse Jiminny Rules DependentRolesRule:17 Serializenuse Jiminny kuLes LiscenerkoLeLannochaveAaminurManagerPermiss1onkuLe• Settingsuse Jiminny Rules ListenerRoleRequiresListenerFeatureRule:N Sidekickuse Jiminny Actlons Updateuserkolesact1on:iM Slackuse Jiminny DiU koles userkolessvnculu:M Teaminsiahtsuse sentry'TimeMemoryMapperTranscrintionclass ScimprovisioningD TranscriptionSummaryM Twiliolpublic function construct.v MConferencoprivate readonly Dispatcher SeventDispatcher.› D ConferenceHandlerorivate readoniv UserService SuserService..MconferenceManager8) SoftPhoneManager.phgprivate readonly RoleChangeLogger SroleChangeLogger,private readonly RoleRepositoryInterface $roleRepository,ConferenceCallbackHandler.onpprivate readpoly eUndateU: Accept File 3- SXP Reject lAferR9lesAqti gor 5 files →@ conferenceManager.phpD1...}Local ChanaesConsole X• Chances 8 tlles, uodatino...= env.local apo→ E Side-by-side viewer@cb4ebf0c config/logging.php@ Constants.php app/Component/SCiMl(C) CoreUserRequest.oho aoo/DTO/SCIMIAAD/RequestiIdnivent =slennonloa!@JiminnvDebuaCommand.phn app/Console/Comma"Level → env "Lub Level', "Into'),php loadina,oho confid@ ScimProvisionina.ohn ann/Comnonent/SCIM'path' => storage_path('logs/Laravel.log').@) SoftPhoneManager.php app/Component/Twilio/Conference/ConferenceManag@ TeytMessaginaService.nhn ann/Services/TelenhonvUnversioned Files 11 files)= .env.nikilocal appenv.other ann(8 CanAcceccAiDonorteTact nhn toctell Init/Doliciod© CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Rf fovicon icn nubliFide tut and0 lictenerDoleCannotHaveAdminOrManagerPermissionRule.php app/RulesT raw sal_query.sal app© RoleAttr.php app/Component/SCIM/Mutators/Attributes/User(@) Cimulot-Wohhaskof.mmond.nhnonnlf.nodlalf.mmondolfrm/LuhonntWorkcnace accociated with hranch 'macter' hac heon rectored /l Pollback II Confiaure (17 minutes aaol=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X556557=562Accept Reject— 564— 565568=579Tx: Auto vTe cri CouT Luura qlOnE TanU UEULaVUUUK LU E 1D.select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team.id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;select x rron aculvity searches where 10 = 10702select * trom activity search tilters where activity search 10 = 109521select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55* from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985071select * trom users where qroup 10 In (5/00:SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:Current vercion• suppont Dally • In 23 m100% 2• Mon 18 May 14:37:38AskJiminnyReportActivityServiceTest v& console [PROD]& console [EU]CascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..Go jiminny_jupiterBISBIS XIA VScimProvisioning.phpThought for 1s>610 tasks done• Update ScimProvisioning to handle roles during user creation* Add validation for SCIM role assignment (listener + admin/manager restriction)Thought for 1sListenerRoleCannotHlaveAdmRoleAttr.ono• RoleAttr.phpScimProvisioning.php) +40|Ask anvthina (%4L)+ @code SWE-1.6Reject allAccept all1ditterenceAAAAMNO'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').W Windsurf Toams 20-1UTF.8io 4 spaces...
|
NULL
|
4653352124179014207
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorFV faVsco. PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js°9 master kProiect© SoftPhoneManager.php© PhoneNumberSecondaryAttr.phC CoreUserRequest.phpscimProvistoning.onpx© PrimaryEmailAttr.php© TextMessagingService.php© RoleAttr.php© SecondaryEmailAttr.phpuse Jiminny DTO\SCIM\AAD \Request \GetUserByQuery:© TitleAttr.phpc usernameAuur.onouse Jiminny DTO\SCIM\AAD \Request \PatchGroup;use Jiminny \Events\Users\UserCreated;© GroupMutator.phpuse Jiminny Models \Group;C UserMutator.phpuse Jiminny Models\JobTitle;c) GroupPatchOperation.phpuse Jiminny Models kole.C) UserPatchOperation.phpuse Jiminny Modets rean,c) constants.ohpuse Jiminny Modeusluser@ Sciminterface.phpuse Jiminny services userservice.c) ScimProvisionina.onouse szninny concracts Act kolekepostcoryencerraceSeedenuse Lulumanare supporc racades valzdacor.Sentryuse Jiminny Rules DependentRolesRule:17 Serializenuse Jiminny kuLes LiscenerkoLeLannochaveAaminurManagerPermiss1onkuLe• Settingsuse Jiminny Rules ListenerRoleRequiresListenerFeatureRule:N Sidekickuse Jiminny Actlons Updateuserkolesact1on:iM Slackuse Jiminny DiU koles userkolessvnculu:M Teaminsiahtsuse sentry'TimeMemoryMapperTranscrintionclass ScimprovisioningD TranscriptionSummaryM Twiliolpublic function construct.v MConferencoprivate readonly Dispatcher SeventDispatcher.› D ConferenceHandlerorivate readoniv UserService SuserService..MconferenceManager8) SoftPhoneManager.phgprivate readonly RoleChangeLogger SroleChangeLogger,private readonly RoleRepositoryInterface $roleRepository,ConferenceCallbackHandler.onpprivate readpoly eUndateU: Accept File 3- SXP Reject lAferR9lesAqti gor 5 files →@ conferenceManager.phpD1...}Local ChanaesConsole X• Chances 8 tlles, uodatino...= env.local apo→ E Side-by-side viewer@cb4ebf0c config/logging.php@ Constants.php app/Component/SCiMl(C) CoreUserRequest.oho aoo/DTO/SCIMIAAD/RequestiIdnivent =slennonloa!@JiminnvDebuaCommand.phn app/Console/Comma"Level → env "Lub Level', "Into'),php loadina,oho confid@ ScimProvisionina.ohn ann/Comnonent/SCIM'path' => storage_path('logs/Laravel.log').@) SoftPhoneManager.php app/Component/Twilio/Conference/ConferenceManag@ TeytMessaginaService.nhn ann/Services/TelenhonvUnversioned Files 11 files)= .env.nikilocal appenv.other ann(8 CanAcceccAiDonorteTact nhn toctell Init/Doliciod© CreateMockAskJiminnyReportResultCommand.php app/Console/Commands/Rf fovicon icn nubliFide tut and0 lictenerDoleCannotHaveAdminOrManagerPermissionRule.php app/RulesT raw sal_query.sal app© RoleAttr.php app/Component/SCIM/Mutators/Attributes/User(@) Cimulot-Wohhaskof.mmond.nhnonnlf.nodlalf.mmondolfrm/LuhonntWorkcnace accociated with hranch 'macter' hac heon rectored /l Pollback II Confiaure (17 minutes aaol=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X556557=562Accept Reject— 564— 565568=579Tx: Auto vTe cri CouT Luura qlOnE TanU UEULaVUUUK LU E 1D.select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team.id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;select x rron aculvity searches where 10 = 10702select * trom activity search tilters where activity search 10 = 109521select * tromorder by 10 descorder by id desc;automated reports where 10 IN (55* from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985071select * trom users where qroup 10 In (5/00:SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:Current vercion• suppont Dally • In 23 m100% 2• Mon 18 May 14:37:38AskJiminnyReportActivityServiceTest v& console [PROD]& console [EU]CascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..Go jiminny_jupiterBISBIS XIA VScimProvisioning.phpThought for 1s>610 tasks done• Update ScimProvisioning to handle roles during user creation* Add validation for SCIM role assignment (listener + admin/manager restriction)Thought for 1sListenerRoleCannotHlaveAdmRoleAttr.ono• RoleAttr.phpScimProvisioning.php) +40|Ask anvthina (%4L)+ @code SWE-1.6Reject allAccept all1ditterenceAAAAMNO'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').W Windsurf Toams 20-1UTF.8io 4 spaces...
|
53097
|
NULL
|
NULL
|
NULL
|
|
53100
|
1860
|
8
|
2026-05-18T11:37:59.445097+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104279445_m1.jpg...
|
PhpStorm
|
faVsco.js – ScimProvisioning.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'...
|
[{"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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-2796147080355312063
|
-8636637127315567163
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
SlackFileEditViewGoHistoryWindowHelpEU (DOCKERDOCKER (docker-compose)81DEV (docker)₴2asticsearchNo Livingconnections1 {"type" : "log""@timestamp":"2026-05-18T11:37:51Z", "tags" : ["error"ticsearch", "data"],"pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}1 {"type": "log","data"],,"@timestamp"'2026-05-18T11:37:51Z'tags": ["error""pid" :7, "message" :"[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}{"type" : "log""@timestamp": "2026-05-18T11:37:52Z", "tags" : ["warning"asticsearch","data"], "pid" :7, "message" : "Unablerevive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:37:52Z"asticsearch", "data"],'"pid":7, "message": "No livingconnections"},"tags" : ["warning", "elkibana{"type" : "log","@timestamp" : "2026-05-18T11:37:52Z", "tags" : ["error", "plugins""taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLivingconnections"}kibana1 {"type" : "log", "@timestamp" : "2026-05-18T11:37:54Z" , "tags" : ["error", "elasticsearch","data"],"pid":7, "message" :"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}kibana1 {"type" : "log""@timestamp" : "2026-05-18T11:37:55Z" , "tags" : ["warning", "elasticsearch", "data"], "pid" :7,"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:37:55Z","tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message"• "No livingconnections"}kibana1 {"type" : "log""@timestamp": "2026-05-18T11:37:55Z""tags" : ["error", "plugins", "taskManager""taskManager"], "pid":7, "message":"Failed to pollfor work: Error: NoLiving connections"}kibana1 {"type" : "log"ticsearch","data"],"@timestamp" : "2026-05-18T11:37:56Z", "tags" : ["error","elas"pid":7, "message":"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch: 9200"}docker_lamp_12026-05-18 11:37:45 Running ['artisan' mailbox:skip-lists:refresh] 12s DONEdocker_lamp_11/fd/1'1 '/usr/local/bin/php' 'artisan' mailbox:skip-lists:refresh > */proc/1 {"type" : "log""@timestamp": "2026-05-18T11:37:58Z", "tags" : ["warning"asticsearch", "data"], "pid":7, "message" : "Unableto revive connection: [URL_WITH_CREDENTIALS] :"2026-05-18T11:37:58Z"asticsearch", "data"], "pid":7, "message": "No living connections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:37:58Z", "tags" : ["error", "plugins", "taskManager",,"taskManager"],"pid" :7,"message": "Failed to poll for work: Error: NoLiving connections"}View in Docker Desktopo View ConfigEnable Watch•HomeDMsActivityFilesLaterMore-Jiminny ...Metrorrtence# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov ". Mario GeorgievNikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiE. Lukas Kovalik y...AppsJira CloudToastSupport Daily • in 23 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas@ Files• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:37:586 0+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMгТОТОвОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
53098
|
NULL
|
NULL
|
NULL
|
|
53101
|
1861
|
10
|
2026-05-18T11:38:03.142479+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104283142_m2.jpg...
|
PhpStorm
|
faVsco.js – CoreUser.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorWindowFV f PhostormVIewINavicarecodeLaravelKeractorWindowFV faVsco.js°9 master k >ProiectC ActivityController.ongC BaseService.php© SoftPhoneManager.php© PhoneNumberSecondaryAttr.ph© PrimaryEmailAttr.php© RoleAttr.php© SecondaryEmailAttr.phpC CoreUserRequest.phpsclmrrovistonine.on.© CoreUser.php X© TitleAttr.phppublic string $phoneNumberPrimary:c usernameAuur.ono© GroupMutator.phppublic string $phoneNumberSecondary:C UserMutator.phpc) GroupPatchOperation.phppublic string $title;C) UserPatchOperation.phpc) constants.ohppublic string $id;Scimintertace.ohpc) ScimProvisionina.onopublic array Sroles:SeedenD Sentry17 Serializenpublic function createFromUser(User Suser): self{...}• SettingsN SidekickiM Slacknubuic tunction createresoonse@: arrav...."M TeaminsiahtsTimeMemoryMapperTranscrintionpublic function cetintleo: strino....-D TranscriptionSummaryM Twiliolv MConferencopublic function setintle strina Stitle): Corelsen-...• M conferenceHandler• MconferenceManager8) SoftPhoneManager.phgprivate function getPhoneNumbersArrayO: arrayt...,ConferenceCallbackHandler.onp1611 editAccept File & X Reject File 0%€+ 6 of 6 files →@ conferenceManager.phpLocal ChangesConsole XL0g x• Chandes & tlles→ E Side-by-side viewer= env.local apo@cb4ebf0c config/logging.php@ Constants.php app/Component/SCIM(C) CoreUser.oho aoo/DTO/SCIMIAAD/Resoonse@ CoreUserRequest.ohp app/DTO/SCIMIAAD/RequestIdnivent =s(C)JiminnvDebuaCommand.oho apo/Console/Commands'level' => env('LOG _LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').pho logdina.nho confidC) ScimProvisioning.ohnann/comnonent/sclMФ) SoftPhoneManager.pho app/Component/Twilio/Conference/ConferenceManag ]:(C) TeytMessaainaService.nhn ann/Services/TelenhonviUnversioned Files 11 files= env nikilocal annl= onv othor ann© CanAccessAiReportsTest.php tests/Unit/Policies(©) CrostoMockAck liminnvDanartDocultCommand.nhnann/Concolo/Commandc/DF0 favicon ico nublid=ids.txt appListenerRoleCannotHaveAdminOrManagerPermissionRule.php app/RulesTa raw sal query.sal app@DoloAtrnhn onnlfamnanont/CAIlA/Mutotoro/Atributoo/lloorWorkcnace accociated with hranch 'macter' hac heon rectored Il Pollback II Confiaure (18 minutec aao)supoont Dally • In z4m100% 2• Mon 18 May 14:38:02U AskJiminnyReportActivityServiceTest vFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..Aссeрс кепесс=custom.log= laravel.log« SF jiminny@localhost]4 HS_local [jiminny@localhost]& console [PROD]& console [EU]A console [STAGING] X556558562563565567568577579Tx: Auto vGo jiminny_jupiterTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE 1D:019 A19 V17 ^ Vselect * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;select * from activity searches where id = 10932select * trom activity search tilters where activity search 10 = 10952*select * tromorder by 10 descresults order by id desc;automated reports where 10 IN (55select * from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985071select * trom users where qroup 10 IN (5/00:SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:CascadeImplement Trial Owne" RoleAttr.ohotnougnis• RoleAttr.phpScimProvisioning.phpThoughts• ScimProvisioning.php7110 tacke done6 more• Add validation for SCIM role ascianment (listener + admin/manader rectriction)• Update CoreUser response to include rolesInoughts• CoreUser.phpAsk anvthina (%4L)+ @code SWE-1.6Reject allAccept all1ditterenceCurrent vercion'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.l0g')'level' => env('LOG LEVEL', 'info').W Windsurf Teams 74:1 UTF-8 P 4 spaces...
|
NULL
|
-6520636623520542305
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormVIewINavicarecodeLaravelKeractorWindowFV f PhostormVIewINavicarecodeLaravelKeractorWindowFV faVsco.js°9 master k >ProiectC ActivityController.ongC BaseService.php© SoftPhoneManager.php© PhoneNumberSecondaryAttr.ph© PrimaryEmailAttr.php© RoleAttr.php© SecondaryEmailAttr.phpC CoreUserRequest.phpsclmrrovistonine.on.© CoreUser.php X© TitleAttr.phppublic string $phoneNumberPrimary:c usernameAuur.ono© GroupMutator.phppublic string $phoneNumberSecondary:C UserMutator.phpc) GroupPatchOperation.phppublic string $title;C) UserPatchOperation.phpc) constants.ohppublic string $id;Scimintertace.ohpc) ScimProvisionina.onopublic array Sroles:SeedenD Sentry17 Serializenpublic function createFromUser(User Suser): self{...}• SettingsN SidekickiM Slacknubuic tunction createresoonse@: arrav...."M TeaminsiahtsTimeMemoryMapperTranscrintionpublic function cetintleo: strino....-D TranscriptionSummaryM Twiliolv MConferencopublic function setintle strina Stitle): Corelsen-...• M conferenceHandler• MconferenceManager8) SoftPhoneManager.phgprivate function getPhoneNumbersArrayO: arrayt...,ConferenceCallbackHandler.onp1611 editAccept File & X Reject File 0%€+ 6 of 6 files →@ conferenceManager.phpLocal ChangesConsole XL0g x• Chandes & tlles→ E Side-by-side viewer= env.local apo@cb4ebf0c config/logging.php@ Constants.php app/Component/SCIM(C) CoreUser.oho aoo/DTO/SCIMIAAD/Resoonse@ CoreUserRequest.ohp app/DTO/SCIMIAAD/RequestIdnivent =s(C)JiminnvDebuaCommand.oho apo/Console/Commands'level' => env('LOG _LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').pho logdina.nho confidC) ScimProvisioning.ohnann/comnonent/sclMФ) SoftPhoneManager.pho app/Component/Twilio/Conference/ConferenceManag ]:(C) TeytMessaainaService.nhn ann/Services/TelenhonviUnversioned Files 11 files= env nikilocal annl= onv othor ann© CanAccessAiReportsTest.php tests/Unit/Policies(©) CrostoMockAck liminnvDanartDocultCommand.nhnann/Concolo/Commandc/DF0 favicon ico nublid=ids.txt appListenerRoleCannotHaveAdminOrManagerPermissionRule.php app/RulesTa raw sal query.sal app@DoloAtrnhn onnlfamnanont/CAIlA/Mutotoro/Atributoo/lloorWorkcnace accociated with hranch 'macter' hac heon rectored Il Pollback II Confiaure (18 minutec aao)supoont Dally • In z4m100% 2• Mon 18 May 14:38:02U AskJiminnyReportActivityServiceTest vFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..Aссeрс кепесс=custom.log= laravel.log« SF jiminny@localhost]4 HS_local [jiminny@localhost]& console [PROD]& console [EU]A console [STAGING] X556558562563565567568577579Tx: Auto vGo jiminny_jupiterTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE 1D:019 A19 V17 ^ Vselect * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;select * from activity searches where id = 10932select * trom activity search tilters where activity search 10 = 10952*select * tromorder by 10 descresults order by id desc;automated reports where 10 IN (55select * from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985071select * trom users where qroup 10 IN (5/00:SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:CascadeImplement Trial Owne" RoleAttr.ohotnougnis• RoleAttr.phpScimProvisioning.phpThoughts• ScimProvisioning.php7110 tacke done6 more• Add validation for SCIM role ascianment (listener + admin/manader rectriction)• Update CoreUser response to include rolesInoughts• CoreUser.phpAsk anvthina (%4L)+ @code SWE-1.6Reject allAccept all1ditterenceCurrent vercion'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.l0g')'level' => env('LOG LEVEL', 'info').W Windsurf Teams 74:1 UTF-8 P 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53102
|
1860
|
9
|
2026-05-18T11:38:30.735998+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104310735_m1.jpg...
|
PhpStorm
|
faVsco.js – CoreUser.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","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}]...
|
-4235983745889776938
|
-8204421443435123770
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
SlackFileEditViewGoHistoryWindowHelpEU (IDOCKER81DEV (docker)₴2DOCKER (docker-compose)"pid":7, "message":"[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}{"type" : "log""@timestamp": "2026-05-18T11:38:22Z", "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "tags" : ["warning"asticsearch", "data"], "pid" :7,"message" : "No livingconnections"}1 {"type": "log""@timestamp": "2026-05-18T11:38:22Z""tags": ["error", "taskManager", "taskManager"], "pid":7, "message":"Failed to pollfor work: Error: NoLiving connections"}docker_lamp_16s DONEdocker_lamp_1'telus'1 '/usr/local/bin/php' 'artisan'activity:sync 'ringcentral' 'avaya''talkdesk'--from='2026-05-18 11:21:00'--to='2026-05-18 11:37:00' > */proc/1/fd/1'2>&1docker_lamp_1docker_lamp_1run_artisan_schedule: Done waiting for schedule:runkibana{"type" : "log","@timestamp": "2026-05-18T11:38:24Z", "tags" : ["error", "elasticsearch","data"], "pid" :7,"message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}1 {"type": "log","@timestamp" : "2026-05-18T11:38:25Z", "tags" : ["warning"asticsearch", "data"], "pid":7, "message" : "Unableto revive connection: [URL_WITH_CREDENTIALS] "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}1 {"type": "log"!"@timestamp": "2026-05-18T11:38:25Z""tags" : ["error","plug, "taskManager","taskManager"], "pid" :7, "message" : "Failed to poll for work: Error: NoLiving connections"}{"type": "log""@timestamp": "2026-05-18T11:38:26Z", "tags" : ["error"ticsearch", "data"], "pid":7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch: 9200"}{"type" :"log""@timestamp": "2026-05-18T11:38:28Z""tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:38:28Z", "tags" : ["warning", "elasticsearch", "data"],"pid":7,"message": "No livingconnections"}kibana1 {"type" : "log"!ins","@timestamp": "2026-05-18T11:38:28Z""tags" : ["error","taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:38:29Z", "tags" : ["error", "elasticsearch", "data"],"pid" :7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}View in Docker Desktop• View ConfigEnable WatchHomeDMsActivityFilesLaterMore→Jiminny ...Metrorrtence# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov ". Mario Georgiev. Nikolay IvanovLo James Graham *8 Stoyan TanevR. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y…..AppsJira CloudToastSupport Daily • in 22 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:38:306 0O Files+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53103
|
1861
|
11
|
2026-05-18T11:38:35.278028+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104315278_m2.jpg...
|
PhpStorm
|
faVsco.js – CoreUser.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings...
|
[{"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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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}]...
|
-4235983745889776938
|
-8204421443435123770
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js°9 master k >roledeyC ActivityController.ongC BaseService.php© SoftPhoneManager.php© SendNudgeExpirationWarningsCommanc(C) CoreUserRequest.onpscimProvistoning.ong© CoreUser.php X© SyncActivity.php© Tracklmported.phpclass loreuser excenas kesponsewhichworkerisworkingOnwnichJoo.ong> C Scheduling(C) Kernel.phppubuic carbon supadtea.ae,public string $externalId;> 0 Contracts• Domainpublic string $phoneNumberPrimary:v DDTOActivitypublic string $phoneNumberSecondary:a customerAopubuic suring sclcler1 Externa ServicelntedrationspubLic scring $107 imoortCallipubuic array sroles"InvitationRolespublic function createFromUser(User Suser): self{...}M SCIMV AAD106 6t>public function createResponse@: arrav....v MRequast© CoreGroupRequest.phppublic function getintleo: strino...(c) CoreUserRequest.one© GetGroupByQuery.phppublic function setiitle(string stitle): CoreUsert...,© GetUserByQuery.php© PatchGroup.php1 usaae© PatchUser.phpprivate function getPhoneNumbersArrayO: arrayt...,vm Docnancog coreGroup.onpcoreuser.onoprivate function getEmailsArrayO: arrayt...eError.php© ListResponse.php* 1of Aedite J,Accept File ** X Reject File 02€+ AofAfiloc →A Doauoct nhnlLocal ChangesShelLOg x• Chandes & tlles= env.local apo→ E Side-by-side viewer@cb4ebf0c config/logging.php@ Constants.php app/Component/SCIM(C) CoreUser.oho aoo/DTO/SCIMIAAD/ResoonseIdnivent =slennonloa!@ CoreUserRequest.pho app/DTO/SCIMIAAD/Request'level' => env('LOG _LEVEL', 'info'),(C)JiminnvDebuaCommand.oho apo/Console/Commands'path' => storage_path('logs/Laravel.log').pho logdina.nho confidC) ScimProvisioning.ohnann/comnonent/sclM@) SoftPhoneManager.nhnann/Comnonent/Twilio/Conference/ConferenceManad(C) TeytMessaainaService.nhn ann/Services/TelenhonviUnversioned Files 11 files= env nikilocal annl= .env.other app© CanAccessAiReportsTest.php tests/Unit/Policies(©) CrostoMockAck liminnvDanartDocultCommand.nhnann/Concolo/Commandc/DF0 favicon ico nublidEids.txt appListenerRoleCannotHaveAdminOrManagerPermissionRule.php app/RulesTa raw sal query.sal app@DoloAtrnhn snnlfamnanont/CAIlA/Mutotoro/Atributoo/lloorWorkcnace accociated with hranch 'macter' hac heon rectored Il Pollback II Confiaure (18 minutec aao)Accept Renectsupoont Dally • In z4m100% 2• Mon 18 May 14:38:34AskJiminnyReportActivityServiceTest vFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X554556558565567568570571573Sa32aTx: AutovTe cri CouT LuuraqOnuE TanU UEULaVUUUK LUE WD:select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team..id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;|select * from activity searches where id = 10932select * trom activity search tilters where activity search 10 = 10952*select * tromorder by 10 descresults order by id desc;automated reports where 10 IN (55* from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985071select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43) = uuid;select * from teams:& console [PROD]& console [EU]CascadeImplement Trial OwneGo jiminny_jupiter019 A19 V17 ^ V• CoreUser.phpThoughts• CoreUser.phpInougnt tor 1s>• CoreUser.php• CoreUser.pheThouaht for 1s )8 10 tasks done• Uodate CoreUser response to include roles© Write unit tests for SCIM role managementThaucht for 1c• ListenerRoleCannotHaveAdminOrManAsk anvthina (%4L)+ @code SWE-1.6Reject allAccept all1ditterenceCurrent vercion'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.l0g')'level' => env('LOG LEVEL', 'info').W Windsurf Teams 74:1 UTF-8 P 4 spaces...
|
53101
|
NULL
|
NULL
|
NULL
|
|
53104
|
1861
|
12
|
2026-05-18T11:38:44.046884+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104324046_m2.jpg...
|
PhpStorm
|
faVsco.js – ListenerRoleCannotHaveAdminOrManagerPe faVsco.js – ListenerRoleCannotHaveAdminOrManagerPermissionRuleTest.php...
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIeWINavicarecodeLaravelKeractorWindowFV f PhostormVIeWINavicarecodeLaravelKeractorWindowFV faVsco.js?9 master k. vroledeyC ActivityController.ong© SendNudgeExpirationWarningsCommanc(C) CoreUserRequest.onpconstants.onpscimProvistoning.ong© CoreUser.php© SyncActivity.phpYC) LictenerPoleCannotHaveAdminOrMana© Tracklmported.php© TextMessagingService.phpwhichworkerisworkingOnwnichJoo.ong> C Scheduling(C) Kernel.php> 0 Contractsdeclare(strict types=1):• DomainvDDTOnamesoace lests Unit Rules:Activitya customerAouse..1 Externa Servicelntedrations7 imoortCalli14»final class ListenerRoleCannotHaveAdmin0rManagerPermissionRuleTest extends TestCaseInvitationD Roles1610 %public function testListenerRoleWithAdminRoleFails: voidt...hn SCIMV AAD35 D >public function testListenerRoleWithManagerRoleFails: void{...}v MRequast© CoreGroupRequest.php© CoreUserRequest.php54 D >public function testListenerRoleWithBothAdminAndManagerRolesFails: void{...}© GetGroupByQuery.php74 D>public function testListenerRoleWithoutAdmin0rManagerPasses: void{...*© GetUserByQuery.phpC) PatchGroup.pnp93 D ›public function testAdminRoleWithoutListenerPasses(: void{...}© PatchUser.phpvm Docnanco112 D ›public function testManagerRoleWithoutListenerPasses0: void{...}g coreGroup.onpcoreuser.ono131public function testAdminAndManagerRolesWithoutListenerPasses0: void{...}eError.phpc)ListResponse.php150public functión testOtherRoli v Accept File *~ . X Reject File 02@ + 7 of 7 filesA Doauoct nhnlLocal ChangesShelLOg x• Chandes & tlles= env.local apo→ E Side-by-side viewerDo not ianorey@cb4ebf0c config/logging.php@ Constants.php app/Component/SCIM(C) CoreUser.oho aoo/DTO/SCIMIAAD/ResoonseIdnivent => lennonloa!@ CoreUserRequest.ohp app/DTO/SCIMIAAD/Request'level' => env('LOG _LEVEL', 'info'),(C)JiminnvDebuaCommand.oho apo/Console/Commands'path' => storage_path('logs/Laravel.log').pho logdina.nho confidC) ScimProvisioning.ohnann/comnonent/sclM@) SoftPhoneManager.nhnann/Comnonent/Twilio/Conference/ConferenceManad(C) TeytMessaainaService.nhn ann/Services/TelenhonviUnversioned Files 12 files= env nikilocal annl= onv othor ann© CanAccessAiReportsTest.php tests/Unit/Policies(©) CrostoMockAck liminnvDanartDocultCommand.nhnann/Concolo/Commandc/DF0 favicon ico nublidEids.txt appListenerRoleCannotHaveAdminOrManagerPermissionRule.php app/Rules0 ListenerRoleCannotHaveAdminOrManagerPermissionRuleTest.php tests/Unit/RP row cal auory cal annWorkcnace accociated with hranch 'macter' hac heon rectored Il Pollback II Confiaure (18 minutec aao)supoont Dally • In z4m100% 2• Mon 18 May 14:38:43U AskJiminnyReportActivityServiceTest vFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..Accept Renect=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X5565585605611'562.566567568569570T571U573—574832a579Tx: AutovTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE WD:select * from teams:SELECT r.* FROM automated_reports rioin teams t on r.team id= t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;|select x rron aculvity searches where 10 = 10702select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descresults order by id desc;automated reports where 10 IN (55select * from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985011select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:Current vercion& console [PROD]& console [EU]CascadeImplement Trial OwneGo jiminny_jupiterBISBIS XIA V• CoreUser.phpThoughts• CoreUser.phpInougnt tor 1s>• CoreUser.phpCoreUser.ohpThouaht for 1s )8 10 tasks done• Uodate CoreUser response to include roles© Write unit tests for SCIM role managementThoucht for 1c%ListenerRoleCannotHaveAdminOrManagerPermissionRulelest.phpAsk anvthina (%4L)+ @code SWE-1.6new v 140/ tokensReject allAccept all1ditterenceAAAAMNO'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.l0g')'level' => env('LOG LEVEL', 'info').W Windsurf Teams 1:1 UTF-8 f 4 spaces...
|
NULL
|
-3990005410750131868
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormVIeWINavicarecodeLaravelKeractorWindowFV f PhostormVIeWINavicarecodeLaravelKeractorWindowFV faVsco.js?9 master k. vroledeyC ActivityController.ong© SendNudgeExpirationWarningsCommanc(C) CoreUserRequest.onpconstants.onpscimProvistoning.ong© CoreUser.php© SyncActivity.phpYC) LictenerPoleCannotHaveAdminOrMana© Tracklmported.php© TextMessagingService.phpwhichworkerisworkingOnwnichJoo.ong> C Scheduling(C) Kernel.php> 0 Contractsdeclare(strict types=1):• DomainvDDTOnamesoace lests Unit Rules:Activitya customerAouse..1 Externa Servicelntedrations7 imoortCalli14»final class ListenerRoleCannotHaveAdmin0rManagerPermissionRuleTest extends TestCaseInvitationD Roles1610 %public function testListenerRoleWithAdminRoleFails: voidt...hn SCIMV AAD35 D >public function testListenerRoleWithManagerRoleFails: void{...}v MRequast© CoreGroupRequest.php© CoreUserRequest.php54 D >public function testListenerRoleWithBothAdminAndManagerRolesFails: void{...}© GetGroupByQuery.php74 D>public function testListenerRoleWithoutAdmin0rManagerPasses: void{...*© GetUserByQuery.phpC) PatchGroup.pnp93 D ›public function testAdminRoleWithoutListenerPasses(: void{...}© PatchUser.phpvm Docnanco112 D ›public function testManagerRoleWithoutListenerPasses0: void{...}g coreGroup.onpcoreuser.ono131public function testAdminAndManagerRolesWithoutListenerPasses0: void{...}eError.phpc)ListResponse.php150public functión testOtherRoli v Accept File *~ . X Reject File 02@ + 7 of 7 filesA Doauoct nhnlLocal ChangesShelLOg x• Chandes & tlles= env.local apo→ E Side-by-side viewerDo not ianorey@cb4ebf0c config/logging.php@ Constants.php app/Component/SCIM(C) CoreUser.oho aoo/DTO/SCIMIAAD/ResoonseIdnivent => lennonloa!@ CoreUserRequest.ohp app/DTO/SCIMIAAD/Request'level' => env('LOG _LEVEL', 'info'),(C)JiminnvDebuaCommand.oho apo/Console/Commands'path' => storage_path('logs/Laravel.log').pho logdina.nho confidC) ScimProvisioning.ohnann/comnonent/sclM@) SoftPhoneManager.nhnann/Comnonent/Twilio/Conference/ConferenceManad(C) TeytMessaainaService.nhn ann/Services/TelenhonviUnversioned Files 12 files= env nikilocal annl= onv othor ann© CanAccessAiReportsTest.php tests/Unit/Policies(©) CrostoMockAck liminnvDanartDocultCommand.nhnann/Concolo/Commandc/DF0 favicon ico nublidEids.txt appListenerRoleCannotHaveAdminOrManagerPermissionRule.php app/Rules0 ListenerRoleCannotHaveAdminOrManagerPermissionRuleTest.php tests/Unit/RP row cal auory cal annWorkcnace accociated with hranch 'macter' hac heon rectored Il Pollback II Confiaure (18 minutec aao)supoont Dally • In z4m100% 2• Mon 18 May 14:38:43U AskJiminnyReportActivityServiceTest vFixing Redis Rate LimSalestorce Token FalliSClM Role Manager+0 ..Accept Renect=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X5565585605611'562.566567568569570T571U573—574832a579Tx: AutovTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE WD:select * from teams:SELECT r.* FROM automated_reports rioin teams t on r.team id= t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires_at IS NULL);select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047;SELECT * FROM social_accounts WHERE sociable_id = 1047;|select x rron aculvity searches where 10 = 10702select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descresults order by id desc;automated reports where 10 IN (55select * from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985011select * trom users where qroup 10 In (5/0):SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43') = uuid;select * from teams:Current vercion& console [PROD]& console [EU]CascadeImplement Trial OwneGo jiminny_jupiterBISBIS XIA V• CoreUser.phpThoughts• CoreUser.phpInougnt tor 1s>• CoreUser.phpCoreUser.ohpThouaht for 1s )8 10 tasks done• Uodate CoreUser response to include roles© Write unit tests for SCIM role managementThoucht for 1c%ListenerRoleCannotHaveAdminOrManagerPermissionRulelest.phpAsk anvthina (%4L)+ @code SWE-1.6new v 140/ tokensReject allAccept all1ditterenceAAAAMNO'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.l0g')'level' => env('LOG LEVEL', 'info').W Windsurf Teams 1:1 UTF-8 f 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53105
|
1861
|
13
|
2026-05-18T11:38:47.061005+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104327061_m2.jpg...
|
PhpStorm
|
faVsco.js – ListenerRoleCannotHaveAdminOrManagerPe faVsco.js – ListenerRoleCannotHaveAdminOrManagerPermissionRuleTest.php...
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Tests\Unit\Rules;
use Illuminate\Translation\PotentiallyTranslatedString;
use Illuminate\Support\Collection;
use Jiminny\Models\Role;
use Jiminny\Models\User;
use Jiminny\Rules\ListenerRoleCannotHaveAdminOrManagerPermissionRule;
use PHPUnit\Framework\TestCase;
final class ListenerRoleCannotHaveAdminOrManagerPermissionRuleTest extends TestCase
{
public function testListenerRoleWithAdminRoleFails(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_LISTENER, 1),
$this->createRole(User::ROLE_ADMIN, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertTrue($failCalled);
}
public function testListenerRoleWithManagerRoleFails(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_LISTENER, 1),
$this->createRole(User::ROLE_MANAGER, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertTrue($failCalled);
}
public function testListenerRoleWithBothAdminAndManagerRolesFails(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_LISTENER, 1),
$this->createRole(User::ROLE_ADMIN, 2),
$this->createRole(User::ROLE_MANAGER, 3),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2, 3], $fail);
$this->assertTrue($failCalled);
}
public function testListenerRoleWithoutAdminOrManagerPasses(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_LISTENER, 1),
$this->createRole(User::ROLE_RECORDER, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertFalse($failCalled);
}
public function testAdminRoleWithoutListenerPasses(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_ADMIN, 1),
$this->createRole(User::ROLE_RECORDER, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertFalse($failCalled);
}
public function testManagerRoleWithoutListenerPasses(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_MANAGER, 1),
$this->createRole(User::ROLE_RECORDER, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertFalse($failCalled);
}
public function testAdminAndManagerRolesWithoutListenerPasses(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_ADMIN, 1),
$this->createRole(User::ROLE_MANAGER, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertFalse($failCalled);
}
public function testOtherRolesPass(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_RECORDER, 1),
$this->createRole(User::ROLE_ANALYST, 2),
$this->createRole(User::ROLE_RECORDER_AND_VOICE, 3),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2, 3], $fail);
$this->assertFalse($failCalled);
}
private function createRole(string $name, int $id): Role
{
$role = new Role();
$role->setAttribute('name', $name);
$role->setAttribute('id', $id);
return $role;
}
}...
|
[{"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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Rules;\n\nuse Illuminate\\Translation\\PotentiallyTranslatedString;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Rules\\ListenerRoleCannotHaveAdminOrManagerPermissionRule;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ListenerRoleCannotHaveAdminOrManagerPermissionRuleTest extends TestCase\n{\n public function testListenerRoleWithAdminRoleFails(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_LISTENER, 1),\n $this->createRole(User::ROLE_ADMIN, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertTrue($failCalled);\n }\n\n public function testListenerRoleWithManagerRoleFails(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_LISTENER, 1),\n $this->createRole(User::ROLE_MANAGER, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertTrue($failCalled);\n }\n\n public function testListenerRoleWithBothAdminAndManagerRolesFails(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_LISTENER, 1),\n $this->createRole(User::ROLE_ADMIN, 2),\n $this->createRole(User::ROLE_MANAGER, 3),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2, 3], $fail);\n\n $this->assertTrue($failCalled);\n }\n\n public function testListenerRoleWithoutAdminOrManagerPasses(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_LISTENER, 1),\n $this->createRole(User::ROLE_RECORDER, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertFalse($failCalled);\n }\n\n public function testAdminRoleWithoutListenerPasses(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_ADMIN, 1),\n $this->createRole(User::ROLE_RECORDER, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertFalse($failCalled);\n }\n\n public function testManagerRoleWithoutListenerPasses(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_MANAGER, 1),\n $this->createRole(User::ROLE_RECORDER, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertFalse($failCalled);\n }\n\n public function testAdminAndManagerRolesWithoutListenerPasses(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_ADMIN, 1),\n $this->createRole(User::ROLE_MANAGER, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertFalse($failCalled);\n }\n\n public function testOtherRolesPass(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_RECORDER, 1),\n $this->createRole(User::ROLE_ANALYST, 2),\n $this->createRole(User::ROLE_RECORDER_AND_VOICE, 3),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2, 3], $fail);\n\n $this->assertFalse($failCalled);\n }\n\n private function createRole(string $name, int $id): Role\n {\n $role = new Role();\n $role->setAttribute('name', $name);\n $role->setAttribute('id', $id);\n\n return $role;\n }\n}","depth":4,"bounds":{"left":0.15990691,"top":0.14684756,"width":0.26728722,"height":0.61452514},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Rules;\n\nuse Illuminate\\Translation\\PotentiallyTranslatedString;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Rules\\ListenerRoleCannotHaveAdminOrManagerPermissionRule;\nuse PHPUnit\\Framework\\TestCase;\n\nfinal class ListenerRoleCannotHaveAdminOrManagerPermissionRuleTest extends TestCase\n{\n public function testListenerRoleWithAdminRoleFails(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_LISTENER, 1),\n $this->createRole(User::ROLE_ADMIN, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertTrue($failCalled);\n }\n\n public function testListenerRoleWithManagerRoleFails(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_LISTENER, 1),\n $this->createRole(User::ROLE_MANAGER, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertTrue($failCalled);\n }\n\n public function testListenerRoleWithBothAdminAndManagerRolesFails(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_LISTENER, 1),\n $this->createRole(User::ROLE_ADMIN, 2),\n $this->createRole(User::ROLE_MANAGER, 3),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2, 3], $fail);\n\n $this->assertTrue($failCalled);\n }\n\n public function testListenerRoleWithoutAdminOrManagerPasses(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_LISTENER, 1),\n $this->createRole(User::ROLE_RECORDER, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertFalse($failCalled);\n }\n\n public function testAdminRoleWithoutListenerPasses(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_ADMIN, 1),\n $this->createRole(User::ROLE_RECORDER, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertFalse($failCalled);\n }\n\n public function testManagerRoleWithoutListenerPasses(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_MANAGER, 1),\n $this->createRole(User::ROLE_RECORDER, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertFalse($failCalled);\n }\n\n public function testAdminAndManagerRolesWithoutListenerPasses(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_ADMIN, 1),\n $this->createRole(User::ROLE_MANAGER, 2),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2], $fail);\n\n $this->assertFalse($failCalled);\n }\n\n public function testOtherRolesPass(): void\n {\n $roles = Collection::make([\n $this->createRole(User::ROLE_RECORDER, 1),\n $this->createRole(User::ROLE_ANALYST, 2),\n $this->createRole(User::ROLE_RECORDER_AND_VOICE, 3),\n ]);\n\n $rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);\n\n $failCalled = false;\n $fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {\n $failCalled = true;\n };\n\n $rule->validate('roles', [1, 2, 3], $fail);\n\n $this->assertFalse($failCalled);\n }\n\n private function createRole(string $name, int $id): Role\n {\n $role = new Role();\n $role->setAttribute('name', $name);\n $role->setAttribute('id', $id);\n\n return $role;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
6644167441708847754
|
8441225656393950942
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
<?php
declare(strict_types=1);
namespace Tests\Unit\Rules;
use Illuminate\Translation\PotentiallyTranslatedString;
use Illuminate\Support\Collection;
use Jiminny\Models\Role;
use Jiminny\Models\User;
use Jiminny\Rules\ListenerRoleCannotHaveAdminOrManagerPermissionRule;
use PHPUnit\Framework\TestCase;
final class ListenerRoleCannotHaveAdminOrManagerPermissionRuleTest extends TestCase
{
public function testListenerRoleWithAdminRoleFails(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_LISTENER, 1),
$this->createRole(User::ROLE_ADMIN, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertTrue($failCalled);
}
public function testListenerRoleWithManagerRoleFails(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_LISTENER, 1),
$this->createRole(User::ROLE_MANAGER, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertTrue($failCalled);
}
public function testListenerRoleWithBothAdminAndManagerRolesFails(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_LISTENER, 1),
$this->createRole(User::ROLE_ADMIN, 2),
$this->createRole(User::ROLE_MANAGER, 3),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2, 3], $fail);
$this->assertTrue($failCalled);
}
public function testListenerRoleWithoutAdminOrManagerPasses(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_LISTENER, 1),
$this->createRole(User::ROLE_RECORDER, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertFalse($failCalled);
}
public function testAdminRoleWithoutListenerPasses(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_ADMIN, 1),
$this->createRole(User::ROLE_RECORDER, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertFalse($failCalled);
}
public function testManagerRoleWithoutListenerPasses(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_MANAGER, 1),
$this->createRole(User::ROLE_RECORDER, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertFalse($failCalled);
}
public function testAdminAndManagerRolesWithoutListenerPasses(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_ADMIN, 1),
$this->createRole(User::ROLE_MANAGER, 2),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2], $fail);
$this->assertFalse($failCalled);
}
public function testOtherRolesPass(): void
{
$roles = Collection::make([
$this->createRole(User::ROLE_RECORDER, 1),
$this->createRole(User::ROLE_ANALYST, 2),
$this->createRole(User::ROLE_RECORDER_AND_VOICE, 3),
]);
$rule = new ListenerRoleCannotHaveAdminOrManagerPermissionRule($roles);
$failCalled = false;
$fail = function (PotentiallyTranslatedString $message) use (&$failCalled): void {
$failCalled = true;
};
$rule->validate('roles', [1, 2, 3], $fail);
$this->assertFalse($failCalled);
}
private function createRole(string $name, int $id): Role
{
$role = new Role();
$role->setAttribute('name', $name);
$role->setAttribute('id', $id);
return $role;
}
}...
|
53104
|
NULL
|
NULL
|
NULL
|
|
53106
|
NULL
|
0
|
2026-05-18T11:39:02.172050+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104342172_m2.jpg...
|
PhpStorm
|
faVsco.js – RoleAttrTest.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
<?php
declare(strict_types=1);
namespace Tests\Unit\Component\SCIM\Mutators\Attributes\User;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Jiminny\Actions\UpdateUserRolesAction;
use Jiminny\Component\Acl\RoleChangeLogger;
use Jiminny\Component\SCIM\Mutators\Attributes\User\RoleAttr;
use Jiminny\Contracts\Acl\RoleRepositoryInterface;
use Jiminny\DTO\Roles\UserRolesSyncDTO;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Role;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use PHPUnit\Framework\TestCase;
use Illuminate\Support\Collection;
final class RoleAttrTest extends TestCase
{
private RoleRepositoryInterface&\PHPUnit\Framework\MockObject\MockObject $roleRepository;
private RoleChangeLogger&\PHPUnit\Framework\MockObject\MockObject $roleChangeLogger;
private UpdateUserRolesAction&\PHPUnit\Framework\MockObject\MockObject $updateUserRolesAction;
private User&\PHPUnit\Framework\MockObject\MockObject $user;
private Team&\PHPUnit\Framework\MockObject\MockObject $team;
protected function setUp(): void
{
$this->roleRepository = $this->createMock(RoleRepositoryInterface::class);
$this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);
$this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);
$this->user = $this->createMock(User::class);
$this->team = $this->createMock(Team::class);
}
public function testDoModificationWithReplaceAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute')
->with($this->callback(function (UserRolesSyncDTO $dto) {
return $dto->user === $this->user
&& $dto->roles->count() === 2;
}));
$mutator = new RoleAttr(
$this->user,
'recorder,analyst',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithAddAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsAdd(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithRemoveRemovesRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$currentRoles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->user->method('roles')->willReturn($currentRoles);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsRemove(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithInvalidRoleLogsWarning(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
Log::shouldReceive('warning')->once();
$mutator = new RoleAttr(
$this->user,
'invalid_role',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithEmptyRolesDoesNothing(): void
{
$roles = Collection::make();
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testHandleDetailReturnsTrue(): void
{
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$result = $mutator->handleDetail();
$this->assertTrue($result);
}
private function createRole(string $name, int $id): Role
{
$role = new Role();
$role->setAttribute('name', $name);
$role->setAttribute('id', $id);
return $role;
}
}...
|
[{"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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Component\\SCIM\\Mutators\\Attributes\\User;\n\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Actions\\UpdateUserRolesAction;\nuse Jiminny\\Component\\Acl\\RoleChangeLogger;\nuse Jiminny\\Component\\SCIM\\Mutators\\Attributes\\User\\RoleAttr;\nuse Jiminny\\Contracts\\Acl\\RoleRepositoryInterface;\nuse Jiminny\\DTO\\Roles\\UserRolesSyncDTO;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse PHPUnit\\Framework\\TestCase;\nuse Illuminate\\Support\\Collection;\n\nfinal class RoleAttrTest extends TestCase\n{\n private RoleRepositoryInterface&\\PHPUnit\\Framework\\MockObject\\MockObject $roleRepository;\n private RoleChangeLogger&\\PHPUnit\\Framework\\MockObject\\MockObject $roleChangeLogger;\n private UpdateUserRolesAction&\\PHPUnit\\Framework\\MockObject\\MockObject $updateUserRolesAction;\n private User&\\PHPUnit\\Framework\\MockObject\\MockObject $user;\n private Team&\\PHPUnit\\Framework\\MockObject\\MockObject $team;\n\n protected function setUp(): void\n {\n $this->roleRepository = $this->createMock(RoleRepositoryInterface::class);\n $this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);\n $this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);\n $this->user = $this->createMock(User::class);\n $this->team = $this->createMock(Team::class);\n }\n\n public function testDoModificationWithReplaceAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute')\n ->with($this->callback(function (UserRolesSyncDTO $dto) {\n return $dto->user === $this->user\n && $dto->roles->count() === 2;\n }));\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder,analyst',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithAddAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsAdd(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithRemoveRemovesRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $currentRoles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->user->method('roles')->willReturn($currentRoles);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsRemove(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithInvalidRoleLogsWarning(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n Log::shouldReceive('warning')->once();\n\n $mutator = new RoleAttr(\n $this->user,\n 'invalid_role',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithEmptyRolesDoesNothing(): void\n {\n $roles = Collection::make();\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n '',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testHandleDetailReturnsTrue(): void\n {\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $result = $mutator->handleDetail();\n\n $this->assertTrue($result);\n }\n\n private function createRole(string $name, int $id): Role\n {\n $role = new Role();\n $role->setAttribute('name', $name);\n $role->setAttribute('id', $id);\n\n return $role;\n }\n}","depth":4,"bounds":{"left":0.15990691,"top":0.14684756,"width":0.26728722,"height":0.7725459},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Component\\SCIM\\Mutators\\Attributes\\User;\n\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Actions\\UpdateUserRolesAction;\nuse Jiminny\\Component\\Acl\\RoleChangeLogger;\nuse Jiminny\\Component\\SCIM\\Mutators\\Attributes\\User\\RoleAttr;\nuse Jiminny\\Contracts\\Acl\\RoleRepositoryInterface;\nuse Jiminny\\DTO\\Roles\\UserRolesSyncDTO;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse PHPUnit\\Framework\\TestCase;\nuse Illuminate\\Support\\Collection;\n\nfinal class RoleAttrTest extends TestCase\n{\n private RoleRepositoryInterface&\\PHPUnit\\Framework\\MockObject\\MockObject $roleRepository;\n private RoleChangeLogger&\\PHPUnit\\Framework\\MockObject\\MockObject $roleChangeLogger;\n private UpdateUserRolesAction&\\PHPUnit\\Framework\\MockObject\\MockObject $updateUserRolesAction;\n private User&\\PHPUnit\\Framework\\MockObject\\MockObject $user;\n private Team&\\PHPUnit\\Framework\\MockObject\\MockObject $team;\n\n protected function setUp(): void\n {\n $this->roleRepository = $this->createMock(RoleRepositoryInterface::class);\n $this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);\n $this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);\n $this->user = $this->createMock(User::class);\n $this->team = $this->createMock(Team::class);\n }\n\n public function testDoModificationWithReplaceAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute')\n ->with($this->callback(function (UserRolesSyncDTO $dto) {\n return $dto->user === $this->user\n && $dto->roles->count() === 2;\n }));\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder,analyst',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithAddAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsAdd(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithRemoveRemovesRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $currentRoles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->user->method('roles')->willReturn($currentRoles);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsRemove(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithInvalidRoleLogsWarning(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n Log::shouldReceive('warning')->once();\n\n $mutator = new RoleAttr(\n $this->user,\n 'invalid_role',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithEmptyRolesDoesNothing(): void\n {\n $roles = Collection::make();\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n '',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testHandleDetailReturnsTrue(): void\n {\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $result = $mutator->handleDetail();\n\n $this->assertTrue($result);\n }\n\n private function createRole(string $name, int $id): Role\n {\n $role = new Role();\n $role->setAttribute('name', $name);\n $role->setAttribute('id', $id);\n\n return $role;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5034228769178112858
|
-7304307323380325979
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
<?php
declare(strict_types=1);
namespace Tests\Unit\Component\SCIM\Mutators\Attributes\User;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Jiminny\Actions\UpdateUserRolesAction;
use Jiminny\Component\Acl\RoleChangeLogger;
use Jiminny\Component\SCIM\Mutators\Attributes\User\RoleAttr;
use Jiminny\Contracts\Acl\RoleRepositoryInterface;
use Jiminny\DTO\Roles\UserRolesSyncDTO;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Role;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use PHPUnit\Framework\TestCase;
use Illuminate\Support\Collection;
final class RoleAttrTest extends TestCase
{
private RoleRepositoryInterface&\PHPUnit\Framework\MockObject\MockObject $roleRepository;
private RoleChangeLogger&\PHPUnit\Framework\MockObject\MockObject $roleChangeLogger;
private UpdateUserRolesAction&\PHPUnit\Framework\MockObject\MockObject $updateUserRolesAction;
private User&\PHPUnit\Framework\MockObject\MockObject $user;
private Team&\PHPUnit\Framework\MockObject\MockObject $team;
protected function setUp(): void
{
$this->roleRepository = $this->createMock(RoleRepositoryInterface::class);
$this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);
$this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);
$this->user = $this->createMock(User::class);
$this->team = $this->createMock(Team::class);
}
public function testDoModificationWithReplaceAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute')
->with($this->callback(function (UserRolesSyncDTO $dto) {
return $dto->user === $this->user
&& $dto->roles->count() === 2;
}));
$mutator = new RoleAttr(
$this->user,
'recorder,analyst',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithAddAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsAdd(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithRemoveRemovesRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$currentRoles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->user->method('roles')->willReturn($currentRoles);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsRemove(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithInvalidRoleLogsWarning(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
Log::shouldReceive('warning')->once();
$mutator = new RoleAttr(
$this->user,
'invalid_role',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithEmptyRolesDoesNothing(): void
{
$roles = Collection::make();
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testHandleDetailReturnsTrue(): void
{
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$result = $mutator->handleDetail();
$this->assertTrue($result);
}
private function createRole(string $name, int $id): Role
{
$role = new Role();
$role->setAttribute('name', $name);
$role->setAttribute('id', $id);
return $role;
}
}...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53107
|
NULL
|
0
|
2026-05-18T11:39:02.737114+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104342737_m1.jpg...
|
PhpStorm
|
faVsco.js – RoleAttrTest.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:...
|
[{"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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","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}]...
|
-6555592100605644990
|
-8204429277455601722
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)DOCKER (docker-compose){"type": "log""@timestamp": "2026-05-18T11:38:52Z"asticsearch", "data"],"No livingconnections"},"tags" : ["warning"I {"type" : "log", "@timestamp" : "2026-05-18T11:38:527","tags" : ["error", "taskManager""taskManager"], "pid" :7, "message":"Failed to pollfor work: Error: NoLiving connections"}{"type" : "log":"2026-05-18T11:38:54Z", "tags" : ['"error""pid":7, "message":"[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}{"type" : "log""@timestamp":"2026-05-18T11:38:55Z", "tags" : ["warning"asticsearch", "data"],"pid" :7,"message": "Unablerevive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:38:55Z", "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message" : "No living1 {"type" : "log"connections"}"@timestamp" : "2026-05-18T11:38:55Z", "tags" : ["error"ins","plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLivingconnections"}kibana{"type" : "log","@timestamp": "2026-05-18T11:38:56Z", "tags" : ["error", "elasticsearch","data"], "pid" :7,"message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}1 {"type": "log","@timestamp" : "2026-05-18T11:38:58Z", "tags" : ["warning"asticsearch", "data"], "pid":7, "message" : "Unableto revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:38:58Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}kibanains"1 {"type": "log"!"@timestamp": "2026-05-18T11:38:58Z""tags" : ["error","plug, "taskManager","taskManager"], "pid" :7, "message" : "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log""@timestamp": "2026-05-18T11:38:59Z","tags" : ["error", "elasticsearch", "data"], "pid":7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch: 9200"}kibana1 {"type" : "log""@timestamp": "2026-05-18T11:39:01Z""tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:39:01Z", "tags" : ["warning", "elasticsearch", "data"],"pid":7,"message": "No livingconnections"}kibana1 {"type" : "log"!ins","@timestamp": "2026-05-18T11:39:01Z","tags" : ["error","plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log", "@timestamp" : "2026-05-18T11:39:02Z", "tags" : ["error", "elasticsearch", "data"],"pid" :7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}View in Docker Desktop• View ConfigEnable Watch•HomeDMsActivityFilesLaterMore→Jiminny ...Metrorrtence# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov ". Mario Georgiev. Nikolay IvanovLo James Graham *8 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily - in 21 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas@ Files• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:39:026 0+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
53102
|
NULL
|
NULL
|
NULL
|
|
53108
|
1863
|
0
|
2026-05-18T11:39:32.518676+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104372518_m2.jpg...
|
PhpStorm
|
faVsco.js – RoleAttrTest.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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}]...
|
8101791824998039176
|
-8348547798392525882
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js°9 master kProiectProphet>@ ProphetAiv DSCIMv D MutatorsC CoreUserRequest.phpscimProvistoning.ong© CoreUser.phpc) RoleAttrTest.php ?C 1extwessagingservice.phpV _ AtributesAccept RenectV D UserC) ActiveAttrlest.php° RoleAttrTest.php© ScimProvisioningTest.phpdeclare(strict types=1)"namesoace lests Unit comoonent Sol rutators Attrzoutes User:>W SettinasSidekick› use ..•D Slack7 Teaminsiahts21 DFinal class RoleAttrest extends TestcaseTranscriotionTranscriotionSummary12 usadecm Twilloprivate RoleRepositoryInterface&\PHPUnit\Framework \Mock0bject \Mock0bject SroleRepository:N Uoloade'D UtilityTM Waveformprivate RoleChangeLogger&\PHPUnit\Framework \Mock0bject\Mock0bject SroleChangeLogger:TN Wehhookc12 usages24 usagesprivate UpdateUserRolesAction&\PHPUnit\Framework \Mock0bject \Mock0bject SupdateUserRolesAction;D ConfigurationiM Consoleprivate User& PHPUnit\Framework\Mock0bject Mock0bject Suser:M Contractsorivace leanx rhrunit rranework nockudeст мосkud ect sceanD DTOm Enums29 @ >protected function setUp: voidf...}C Events38 D >exceptions0) fixturesMcuarde73 D >pubLic tunction testboMod1t1cat1onw1thkeplaceAss1qnskoleso: vo1d ...-Accept File & X Reject File 0x@+ 8 of 8 files →public function testDoModificationWithAddAssiqnsRoles: void{...}Local ChangesLog x• Chandes & tlles+→ E Side-by-side viewer y= env.local apo@cb4ebf0c config/logging.php• Constants.php app/Component/SCIM(C) CoreUser.oho aoo/DTO/SCIMIAAD/Resoonse@ CoreUserRequest.pho app/DTO/SCIMIAAD/Request(C)JiminnvDebuaCommand.oho apo/Console/Commands'driver' => 'errorlog''level' => env('LOG _LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').pho logdina.nho confidC) ScimProvisioning.ohnann/comnonent/sclM@) SoftPhoneManager.nhnann/Comnonent/Twilio/Conference/ConferenceManad(C) TeytMessaainaService.nhn ann/Services/TelenhonviUnversioned Files 12 files= env nikilocal annl= .env.other app© CanAccessAiReportsTest.php tests/Unit/Policies(e) CrostoMockAck liminnvDonartDocultCommand.nhnann/Concolo/Commandc/pF0 favicon ico nublid=ids.txt appListenerRoleCannotHaveAdminOrManagerPermissionRule.php app/Rules0 ListenerRoleCannotHaveAdminOrManagerPermissionRuleTest.php tests/Unit/RP row cal auory cal annWorkcnace accociated with hranch 'macter' hac heon rectored Il Pollback II Confiaure (19 minutes aao)=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X556557* 558560•561|-562565567568569570575577579Tx: Auto vTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE 1D:select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team.id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires at IS NULL):select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;|select x Tron aculvity searches where 10 = 10752select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (555* from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985071select * trom users where qroup 10 IN (5/00:SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43) = uuid;select * from teams:"suppont Dally • In 21m100% 2• Mon 18 May 14:39:32AskJiminnyReportActivityServiceTest v& console [PROD]# console [euyCascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token Falli• SClM Role Manager+0 ..Go jiminny_jupiter• CoreUser.php019 A19 V17 ^ V8/10 tasks done• Update CoreUser response to include roles(9) Write unit tests for SCIM role management• ListenerRoleCannotHave)C PoleAttrTect nhiO oho artisan test --filter ListenerRoleCannotHaveAdmin0rManagerPermissionRuletestAsk anvthina (%4L)+ @code SWE-1.6RunX+ SkipReject allAccept all1ditterenceCurrent vercion'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path( 'logs/Laravel.log')'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').WN Windsurf Toams 1-1UTF.8io 4 spaces ©...
|
53106
|
NULL
|
NULL
|
NULL
|
|
53109
|
1862
|
0
|
2026-05-18T11:39:34.000455+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104374000_m1.jpg...
|
PhpStorm
|
faVsco.js – RoleAttrTest.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","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}]...
|
-5187215517464698470
|
-8348545152461722172
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴2DOCKER (docker-compose)kibana{"type" : "log"asticsearch""@timestamp" : "2026-05-18T11:39:25Z" , "tags" : ["warning""elh:9200/"},"data"], "pid":7,'"message" : "Unabletoreviveconnection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:39:25Z", "tags" : ["warning".asticsearch", "data"],"pid":7,"message": "No livingconnections "}kibana1 {"type" : "log".ins""taskManager","@timestamp" : "2026-05-18T11:39:25Z""tags" : ["error""plug"taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLivingconnections"}docker_lamp_12026-05-18 11:39:23 Running ['artisan'mailbox:batch:process --max-batches=15JZS DONEdocker_lamp_11 '/usr/local/bin/php' 'artisan' mailbox:batch:process --max-batches=15 > '/proc/1/fd/1' 2>&1"@timestamp" : "2026-05-18T11:39:26Z", "tags" : ["error", "elas"pid":7, "message" :"[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch: 9200"}{"type" : "log""@timestamp": "2026-05-18T11:39:28Z","tags" : ["warning", "elasticsearch","data"], "pid" :7,"message" : "Unableconnection: [URL_WITH_CREDENTIALS] "tags" : ["warning", "elasticsearch", "data"], "pid" :7,"message": "No livingconnections"}kibanains"1 {"type" : "log""@timestamp":"2026-05-18T11:39:28Z""tags" : ["error","plug, "taskManager","taskManager"], "pid" :7, "message" : "Failed to pollLivingconnections"}for work: Error: Nokibana1 {"type" : "log""@timestamp": "2026-05-18T11:39:29Z","tags" : ["error", "elasticsearch",,"data"],"pid":7,'"message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch: 9200"}1 {"type" : "log""@timestamp" : "2026-05-18T11:39:31Z" , "tags" : ["warning"asticsearch", "data"],"pid":7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:39:31Z", "tags" : ["warning", "elasticsearch", "data"],"pid":7,"message": "No livingconnections"}1 {"type" : "log"."@timestamp": "2026-05-18T11:39:31Z""tags" : ["error", "plug, "taskManager","taskManager"], "pid":7, "message": "Failed to poll for work: Error: Noconnections"}docker_lamp_12026-05-18 11:39:26 Running ['artisan' activity:aircall:check-and-rendocker_lamp_11 '/usr/local/bin/php' 'artisan' activity:aircall:check-and-renew > '/proc/1/fd/1' 2>&1kibana1 {"type" : "log","@timestamp" : "2026-05-18T11:39:32Z", "tags" : ["error", "elasticsearch", "data"],"pid" :7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}View in Docker Desktopo View ConfigEnable WatchHomeDMsActivityFilesLaterMore-Jiminny ...Metrorrtence# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...• Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka StoyanovaStoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov ". Mario GeorgievNikolay IvanovLo James Graham *2 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiE. Lukas Kovalik y...AppsJira CloudToastSupport Daily - in 21 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas@ Files• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:8• Mon 18 May 14:39:336 0+Sign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53110
|
1863
|
1
|
2026-05-18T11:40:04.774068+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104404774_m2.jpg...
|
PhpStorm
|
faVsco.js – RoleAttrTest.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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}]...
|
8101791824998039176
|
-8348547798392525882
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js°9 master kProiectProphet>@ ProphetAiv DSCIMv D MutatorsC CoreUserRequest.phpscimProvistoning.ong© CoreUser.phpc) RoleAttrTest.php ?C 1extwessagingservice.phpV _ AtributesAccept RenectV D UserC) ActiveAttrlest.php° RoleAttrTest.php© ScimProvisioningTest.phpdeclare(strict types=1)"namesoace lests Unit comoonent Sol rutators Attrzoutes User:>W SettinasSidekick› use ..•D Slack7 Teaminsiahts21 DFinal class RoleAttrest extends TestcaseTranscriotionTranscriotionSummary12 usadecm Twilloprivate RoleRepositoryInterface&\PHPUnit\Framework \Mock0bject \Mock0bject SroleRepository:N Uoloade'D UtilityTM Waveformprivate RoleChangeLogger&\PHPUnit\Framework \Mock0bject\Mock0bject SroleChangeLogger:TN Wehhookc12 usages24 usagesprivate UpdateUserRolesAction&\PHPUnit\Framework \Mock0bject \Mock0bject SupdateUserRolesAction;D ConfigurationiM Consoleprivate User& PHPUnit\Framework\Mock0bject Mock0bject Suser:M Contractsorivace leanx rhrunit rranework nockudeст мосkud ect sceanD DTOm Enums29 @ >protected function setUp: voidf...}C Events38 D >exceptions0) fixturesMcuarde73 D >pubLic tunction testboMod1t1cat1onw1thkeplaceAss1qnskoleso: vo1d ...-Accept File & X Reject File 0x@+ 8 of 8 files →public function testDoModificationWithAddAssiqnsRoles: void{...}Local ChangesLog x• Chandes & tlles+→ E Side-by-side viewer y= env.local apo@cb4ebf0c config/logging.php• Constants.php app/Component/SCIM(C) CoreUser.oho aoo/DTO/SCIMIAAD/Resoonse@ CoreUserRequest.pho app/DTO/SCIMIAAD/Request(C)JiminnvDebuaCommand.oho apo/Console/Commands'driver' => 'errorlog''level' => env('LOG _LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').pho logdina.nho confidC) ScimProvisioning.ohnann/comnonent/sclM@) SoftPhoneManager.nhnann/Comnonent/Twilio/Conference/ConferenceManad(C) TeytMessaainaService.nhn ann/Services/TelenhonviUnversioned Files 12 files= env nikilocal annl= .env.other app© CanAccessAiReportsTest.php tests/Unit/Policies(e) CrostoMockAck liminnvDonartDocultCommand.nhnann/Concolo/Commandc/pF0 favicon ico nublid=ids.txt appListenerRoleCannotHaveAdminOrManagerPermissionRule.php app/Rules0 ListenerRoleCannotHaveAdminOrManagerPermissionRuleTest.php tests/Unit/RP row cal auory cal annWorkcnace accociated with hranch 'macter' hac heon rectored Il Pollback II Confiaure (20 minutes aao)=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X556557* 558560•561|-562565567568569570575577579Tx: Auto vTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE 1D:select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team.id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires at IS NULL):select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;|select x Tron aculvity searches where 10 = 10752select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (555* from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985071select * trom users where qroup 10 IN (5/00:SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43) = uuid;select * from teams:• suppont Dally • In 20 m100% 2• Mon 18 May 14:40:04AskJiminnyReportActivityServiceTest v& console [PROD]# console [euyCascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token Falli• SClM Role Manager+0 ..Go jiminny_jupiter• CoreUser.php019 A19 V17 ^ V8/10 tasks done• Update CoreUser response to include roles(9) Write unit tests for SCIM role management• ListenerRoleCannotHave)C PoleAttrTect nhiO oho artisan test --filter ListenerRoleCannotHaveAdmin0rManagerPermissionRuletestAsk anvthina (%4L)+ @code SWE-1.6RunX+ Skip2) Reject allAccept all1ditterenceCurrent vercion'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path( 'logs/Laravel.log')'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').WN Windsurf Toams 1-1UTF.8io 4 spaces ©...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53111
|
1862
|
1
|
2026-05-18T11:40:05.803475+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104405803_m1.jpg...
|
PhpStorm
|
faVsco.js – RoleAttrTest.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'...
|
[{"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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-4047408223668449518
|
-8636777727364969024
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)XIDOCKER (docker-compose){"type": "log""@timestamp" : "2026-05-18T11:39:55Z", "tags" : ["warning"asticsearch", "data"],"No livingconnections"}I {"type" : "log", "@timestamp" : "2026-05-18T11:39:55Z","tags" : ["error", "taskManager""taskManager"], "pid" :7,"Failed to pollfor work: Error: Noconnections"}{"type" : "log":"2026-05-18T11:39:57Z", "tags" : ['"error""pid":7, "message":"[ConnectionError]: getaddrinfo ENOTFOUND elasticseelasticsearch:9200"}{"type" : "log""@timestamp":"2026-05-18T11:39:58Z", "tags" : ["warning"asticsearch", "data"], "pid" :7,"message": "Unablerevive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:39:58Z", "tags" : ["warning", "elasticsearch", "data"],"pid" :7,"message" : "No livingkibana1 {"type" : "log"connections"}"@timestamp" : "2026-05-18T11:39:58Z", "tags" : ["error"ins","plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLivingconnections"}kibana{"type" : "log","@timestamp": "2026-05-18T11:39:59Z","tags" : ["error", "elasticsearch","data"], "pid" :7,"message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}1 {"type": "log", "@timestamp" : "2026-05-18T11:40:01Z", "tags" : ["warning"asticsearch", "data"], "pid":7, "message" : "Unableto revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:40:01Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "No living connections"}kibanains"1 {"type": "log"!"@timestamp" : "2026-05-18T11:40:01Z", "tags" : ["error","plug, "taskManager","taskManager"], "pid" :7, "message" : "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log""@timestamp" : "2026-05-18T11:40:02Z", "tags" : ["error", "elasticsearch", "data"], "pid":7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch: 9200"}kibana1 {"type" : "log""@timestamp": "2026-05-18T11:40:04Z""tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:40:04Z", "tags" : ["warning", "elasticsearch", "data"],"pid":7,"message": "No livingconnections"}kibana1 {"type" : "log"!ins","@timestamp": "2026-05-18T11:40:04Z""tags" : ["error","plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log", "@timestamp" : "2026-05-18T11:40: 04Z", "tags" : ["error", "elasticsearch", "data"],"pid" :7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}View in Docker Desktop• View ConfigEnable Watch•HomeDMsActivityFilesLaterMore-Jiminny ...Metrorrtence# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...• Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka StoyanovaStoyan Tomov€. Vasil Vasilev% Galya DimitrovaLa Todor Stamatov ". Mario Georgiev. Nikolay IvanovLo James Graham *8 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiLukas Kovalik y...AppsJira CloudToastSupport Daily • in 20 m100% <78• Mon 18 May 14:40:05Describe what you are looking forNikolay Yankov6 0MessagesAdd canvas@ Files+• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OSign in with GoogleLet's Get Started:не ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMгТОТОвОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
53109
|
NULL
|
NULL
|
NULL
|
|
53112
|
1863
|
2
|
2026-05-18T11:40:36.622182+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104436622_m2.jpg...
|
PhpStorm
|
faVsco.js – RoleAttrTest.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
<?php
declare(strict_types=1);
namespace Tests\Unit\Component\SCIM\Mutators\Attributes\User;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Jiminny\Actions\UpdateUserRolesAction;
use Jiminny\Component\Acl\RoleChangeLogger;
use Jiminny\Component\SCIM\Mutators\Attributes\User\RoleAttr;
use Jiminny\Contracts\Acl\RoleRepositoryInterface;
use Jiminny\DTO\Roles\UserRolesSyncDTO;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Role;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use PHPUnit\Framework\TestCase;
use Illuminate\Support\Collection;
final class RoleAttrTest extends TestCase
{
private RoleRepositoryInterface&\PHPUnit\Framework\MockObject\MockObject $roleRepository;
private RoleChangeLogger&\PHPUnit\Framework\MockObject\MockObject $roleChangeLogger;
private UpdateUserRolesAction&\PHPUnit\Framework\MockObject\MockObject $updateUserRolesAction;
private User&\PHPUnit\Framework\MockObject\MockObject $user;
private Team&\PHPUnit\Framework\MockObject\MockObject $team;
protected function setUp(): void
{
$this->roleRepository = $this->createMock(RoleRepositoryInterface::class);
$this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);
$this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);
$this->user = $this->createMock(User::class);
$this->team = $this->createMock(Team::class);
}
public function testDoModificationWithReplaceAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute')
->with($this->callback(function (UserRolesSyncDTO $dto) {
return $dto->user === $this->user
&& $dto->roles->count() === 2;
}));
$mutator = new RoleAttr(
$this->user,
'recorder,analyst',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithAddAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsAdd(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithRemoveRemovesRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$currentRoles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->user->method('roles')->willReturn($currentRoles);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsRemove(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithInvalidRoleLogsWarning(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
Log::shouldReceive('warning')->once();
$mutator = new RoleAttr(
$this->user,
'invalid_role',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithEmptyRolesDoesNothing(): void
{
$roles = Collection::make();
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testHandleDetailReturnsTrue(): void
{
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$result = $mutator->handleDetail();
$this->assertTrue($result);
}
private function createRole(string $name, int $id): Role
{
$role = new Role();
$role->setAttribute('name', $name);
$role->setAttribute('id', $id);
return $role;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…...
|
[{"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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Component\\SCIM\\Mutators\\Attributes\\User;\n\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Actions\\UpdateUserRolesAction;\nuse Jiminny\\Component\\Acl\\RoleChangeLogger;\nuse Jiminny\\Component\\SCIM\\Mutators\\Attributes\\User\\RoleAttr;\nuse Jiminny\\Contracts\\Acl\\RoleRepositoryInterface;\nuse Jiminny\\DTO\\Roles\\UserRolesSyncDTO;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse PHPUnit\\Framework\\TestCase;\nuse Illuminate\\Support\\Collection;\n\nfinal class RoleAttrTest extends TestCase\n{\n private RoleRepositoryInterface&\\PHPUnit\\Framework\\MockObject\\MockObject $roleRepository;\n private RoleChangeLogger&\\PHPUnit\\Framework\\MockObject\\MockObject $roleChangeLogger;\n private UpdateUserRolesAction&\\PHPUnit\\Framework\\MockObject\\MockObject $updateUserRolesAction;\n private User&\\PHPUnit\\Framework\\MockObject\\MockObject $user;\n private Team&\\PHPUnit\\Framework\\MockObject\\MockObject $team;\n\n protected function setUp(): void\n {\n $this->roleRepository = $this->createMock(RoleRepositoryInterface::class);\n $this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);\n $this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);\n $this->user = $this->createMock(User::class);\n $this->team = $this->createMock(Team::class);\n }\n\n public function testDoModificationWithReplaceAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute')\n ->with($this->callback(function (UserRolesSyncDTO $dto) {\n return $dto->user === $this->user\n && $dto->roles->count() === 2;\n }));\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder,analyst',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithAddAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsAdd(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithRemoveRemovesRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $currentRoles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->user->method('roles')->willReturn($currentRoles);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsRemove(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithInvalidRoleLogsWarning(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n Log::shouldReceive('warning')->once();\n\n $mutator = new RoleAttr(\n $this->user,\n 'invalid_role',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithEmptyRolesDoesNothing(): void\n {\n $roles = Collection::make();\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n '',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testHandleDetailReturnsTrue(): void\n {\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $result = $mutator->handleDetail();\n\n $this->assertTrue($result);\n }\n\n private function createRole(string $name, int $id): Role\n {\n $role = new Role();\n $role->setAttribute('name', $name);\n $role->setAttribute('id', $id);\n\n return $role;\n }\n}","depth":4,"bounds":{"left":0.15990691,"top":0.14684756,"width":0.26728722,"height":0.7725459},"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Component\\SCIM\\Mutators\\Attributes\\User;\n\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Actions\\UpdateUserRolesAction;\nuse Jiminny\\Component\\Acl\\RoleChangeLogger;\nuse Jiminny\\Component\\SCIM\\Mutators\\Attributes\\User\\RoleAttr;\nuse Jiminny\\Contracts\\Acl\\RoleRepositoryInterface;\nuse Jiminny\\DTO\\Roles\\UserRolesSyncDTO;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse PHPUnit\\Framework\\TestCase;\nuse Illuminate\\Support\\Collection;\n\nfinal class RoleAttrTest extends TestCase\n{\n private RoleRepositoryInterface&\\PHPUnit\\Framework\\MockObject\\MockObject $roleRepository;\n private RoleChangeLogger&\\PHPUnit\\Framework\\MockObject\\MockObject $roleChangeLogger;\n private UpdateUserRolesAction&\\PHPUnit\\Framework\\MockObject\\MockObject $updateUserRolesAction;\n private User&\\PHPUnit\\Framework\\MockObject\\MockObject $user;\n private Team&\\PHPUnit\\Framework\\MockObject\\MockObject $team;\n\n protected function setUp(): void\n {\n $this->roleRepository = $this->createMock(RoleRepositoryInterface::class);\n $this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);\n $this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);\n $this->user = $this->createMock(User::class);\n $this->team = $this->createMock(Team::class);\n }\n\n public function testDoModificationWithReplaceAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute')\n ->with($this->callback(function (UserRolesSyncDTO $dto) {\n return $dto->user === $this->user\n && $dto->roles->count() === 2;\n }));\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder,analyst',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithAddAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsAdd(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithRemoveRemovesRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $currentRoles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->user->method('roles')->willReturn($currentRoles);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsRemove(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithInvalidRoleLogsWarning(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n Log::shouldReceive('warning')->once();\n\n $mutator = new RoleAttr(\n $this->user,\n 'invalid_role',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithEmptyRolesDoesNothing(): void\n {\n $roles = Collection::make();\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n '',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testHandleDetailReturnsTrue(): void\n {\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $result = $mutator->handleDetail();\n\n $this->assertTrue($result);\n }\n\n private function createRole(string $name, int $id): Role\n {\n $role = new Role();\n $role->setAttribute('name', $name);\n $role->setAttribute('id', $id);\n\n return $role;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.42785904,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.43650267,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.4474734,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.45611703,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.46476063,"top":0.09896249,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8859367993221888330
|
-8456102928079281755
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
<?php
declare(strict_types=1);
namespace Tests\Unit\Component\SCIM\Mutators\Attributes\User;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Jiminny\Actions\UpdateUserRolesAction;
use Jiminny\Component\Acl\RoleChangeLogger;
use Jiminny\Component\SCIM\Mutators\Attributes\User\RoleAttr;
use Jiminny\Contracts\Acl\RoleRepositoryInterface;
use Jiminny\DTO\Roles\UserRolesSyncDTO;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Role;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use PHPUnit\Framework\TestCase;
use Illuminate\Support\Collection;
final class RoleAttrTest extends TestCase
{
private RoleRepositoryInterface&\PHPUnit\Framework\MockObject\MockObject $roleRepository;
private RoleChangeLogger&\PHPUnit\Framework\MockObject\MockObject $roleChangeLogger;
private UpdateUserRolesAction&\PHPUnit\Framework\MockObject\MockObject $updateUserRolesAction;
private User&\PHPUnit\Framework\MockObject\MockObject $user;
private Team&\PHPUnit\Framework\MockObject\MockObject $team;
protected function setUp(): void
{
$this->roleRepository = $this->createMock(RoleRepositoryInterface::class);
$this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);
$this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);
$this->user = $this->createMock(User::class);
$this->team = $this->createMock(Team::class);
}
public function testDoModificationWithReplaceAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute')
->with($this->callback(function (UserRolesSyncDTO $dto) {
return $dto->user === $this->user
&& $dto->roles->count() === 2;
}));
$mutator = new RoleAttr(
$this->user,
'recorder,analyst',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithAddAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsAdd(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithRemoveRemovesRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$currentRoles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->user->method('roles')->willReturn($currentRoles);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsRemove(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithInvalidRoleLogsWarning(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
Log::shouldReceive('warning')->once();
$mutator = new RoleAttr(
$this->user,
'invalid_role',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithEmptyRolesDoesNothing(): void
{
$roles = Collection::make();
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testHandleDetailReturnsTrue(): void
{
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$result = $mutator->handleDetail();
$this->assertTrue($result);
}
private function createRole(string $name, int $id): Role
{
$role = new Role();
$role->setAttribute('name', $name);
$role->setAttribute('id', $id);
return $role;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…...
|
53110
|
NULL
|
NULL
|
NULL
|
|
53113
|
1862
|
2
|
2026-05-18T11:40:37.471024+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104437471_m1.jpg...
|
PhpStorm
|
faVsco.js – RoleAttrTest.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
<?php
declare(strict_types=1);
namespace Tests\Unit\Component\SCIM\Mutators\Attributes\User;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Jiminny\Actions\UpdateUserRolesAction;
use Jiminny\Component\Acl\RoleChangeLogger;
use Jiminny\Component\SCIM\Mutators\Attributes\User\RoleAttr;
use Jiminny\Contracts\Acl\RoleRepositoryInterface;
use Jiminny\DTO\Roles\UserRolesSyncDTO;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Role;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use PHPUnit\Framework\TestCase;
use Illuminate\Support\Collection;
final class RoleAttrTest extends TestCase
{
private RoleRepositoryInterface&\PHPUnit\Framework\MockObject\MockObject $roleRepository;
private RoleChangeLogger&\PHPUnit\Framework\MockObject\MockObject $roleChangeLogger;
private UpdateUserRolesAction&\PHPUnit\Framework\MockObject\MockObject $updateUserRolesAction;
private User&\PHPUnit\Framework\MockObject\MockObject $user;
private Team&\PHPUnit\Framework\MockObject\MockObject $team;
protected function setUp(): void
{
$this->roleRepository = $this->createMock(RoleRepositoryInterface::class);
$this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);
$this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);
$this->user = $this->createMock(User::class);
$this->team = $this->createMock(Team::class);
}
public function testDoModificationWithReplaceAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute')
->with($this->callback(function (UserRolesSyncDTO $dto) {
return $dto->user === $this->user
&& $dto->roles->count() === 2;
}));
$mutator = new RoleAttr(
$this->user,
'recorder,analyst',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithAddAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsAdd(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithRemoveRemovesRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$currentRoles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->user->method('roles')->willReturn($currentRoles);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsRemove(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithInvalidRoleLogsWarning(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
Log::shouldReceive('warning')->once();
$mutator = new RoleAttr(
$this->user,
'invalid_role',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithEmptyRolesDoesNothing(): void
{
$roles = Collection::make();
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testHandleDetailReturnsTrue(): void
{
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$result = $mutator->handleDetail();
$this->assertTrue($result);
}
private function createRole(string $name, int $id): Role
{
$role = new Role();
$role->setAttribute('name', $name);
$role->setAttribute('id', $id);
return $role;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny_jupiter
Sync Changes
Hide This Notification
Code changed:
Hide
19
19
17
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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","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":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Component\\SCIM\\Mutators\\Attributes\\User;\n\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Actions\\UpdateUserRolesAction;\nuse Jiminny\\Component\\Acl\\RoleChangeLogger;\nuse Jiminny\\Component\\SCIM\\Mutators\\Attributes\\User\\RoleAttr;\nuse Jiminny\\Contracts\\Acl\\RoleRepositoryInterface;\nuse Jiminny\\DTO\\Roles\\UserRolesSyncDTO;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse PHPUnit\\Framework\\TestCase;\nuse Illuminate\\Support\\Collection;\n\nfinal class RoleAttrTest extends TestCase\n{\n private RoleRepositoryInterface&\\PHPUnit\\Framework\\MockObject\\MockObject $roleRepository;\n private RoleChangeLogger&\\PHPUnit\\Framework\\MockObject\\MockObject $roleChangeLogger;\n private UpdateUserRolesAction&\\PHPUnit\\Framework\\MockObject\\MockObject $updateUserRolesAction;\n private User&\\PHPUnit\\Framework\\MockObject\\MockObject $user;\n private Team&\\PHPUnit\\Framework\\MockObject\\MockObject $team;\n\n protected function setUp(): void\n {\n $this->roleRepository = $this->createMock(RoleRepositoryInterface::class);\n $this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);\n $this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);\n $this->user = $this->createMock(User::class);\n $this->team = $this->createMock(Team::class);\n }\n\n public function testDoModificationWithReplaceAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute')\n ->with($this->callback(function (UserRolesSyncDTO $dto) {\n return $dto->user === $this->user\n && $dto->roles->count() === 2;\n }));\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder,analyst',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithAddAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsAdd(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithRemoveRemovesRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $currentRoles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->user->method('roles')->willReturn($currentRoles);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsRemove(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithInvalidRoleLogsWarning(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n Log::shouldReceive('warning')->once();\n\n $mutator = new RoleAttr(\n $this->user,\n 'invalid_role',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithEmptyRolesDoesNothing(): void\n {\n $roles = Collection::make();\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n '',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testHandleDetailReturnsTrue(): void\n {\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $result = $mutator->handleDetail();\n\n $this->assertTrue($result);\n }\n\n private function createRole(string $name, int $id): Role\n {\n $role = new Role();\n $role->setAttribute('name', $name);\n $role->setAttribute('id', $id);\n\n return $role;\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Component\\SCIM\\Mutators\\Attributes\\User;\n\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\Facades\\Validator;\nuse Jiminny\\Actions\\UpdateUserRolesAction;\nuse Jiminny\\Component\\Acl\\RoleChangeLogger;\nuse Jiminny\\Component\\SCIM\\Mutators\\Attributes\\User\\RoleAttr;\nuse Jiminny\\Contracts\\Acl\\RoleRepositoryInterface;\nuse Jiminny\\DTO\\Roles\\UserRolesSyncDTO;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\Role;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse PHPUnit\\Framework\\TestCase;\nuse Illuminate\\Support\\Collection;\n\nfinal class RoleAttrTest extends TestCase\n{\n private RoleRepositoryInterface&\\PHPUnit\\Framework\\MockObject\\MockObject $roleRepository;\n private RoleChangeLogger&\\PHPUnit\\Framework\\MockObject\\MockObject $roleChangeLogger;\n private UpdateUserRolesAction&\\PHPUnit\\Framework\\MockObject\\MockObject $updateUserRolesAction;\n private User&\\PHPUnit\\Framework\\MockObject\\MockObject $user;\n private Team&\\PHPUnit\\Framework\\MockObject\\MockObject $team;\n\n protected function setUp(): void\n {\n $this->roleRepository = $this->createMock(RoleRepositoryInterface::class);\n $this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);\n $this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);\n $this->user = $this->createMock(User::class);\n $this->team = $this->createMock(Team::class);\n }\n\n public function testDoModificationWithReplaceAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute')\n ->with($this->callback(function (UserRolesSyncDTO $dto) {\n return $dto->user === $this->user\n && $dto->roles->count() === 2;\n }));\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder,analyst',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithAddAssignsRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsAdd(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithRemoveRemovesRoles(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $currentRoles = Collection::make([\n $this->createRole('recorder', 1),\n $this->createRole('analyst', 2),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->user->method('roles')->willReturn($currentRoles);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->once())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsRemove(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithInvalidRoleLogsWarning(): void\n {\n $roles = Collection::make([\n $this->createRole('recorder', 1),\n ]);\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n Log::shouldReceive('warning')->once();\n\n $mutator = new RoleAttr(\n $this->user,\n 'invalid_role',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testDoModificationWithEmptyRolesDoesNothing(): void\n {\n $roles = Collection::make();\n\n $this->user->method('getTeam')->willReturn($this->team);\n $this->user->method('getId')->willReturn(123);\n $this->team->method('getId')->willReturn(456);\n\n $this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);\n\n $this->updateUserRolesAction->expects($this->never())\n ->method('execute');\n\n $mutator = new RoleAttr(\n $this->user,\n '',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $mutator->setIsReplace(true);\n $result = $mutator->doModification();\n\n $this->assertSame($this->user, $result);\n }\n\n public function testHandleDetailReturnsTrue(): void\n {\n $mutator = new RoleAttr(\n $this->user,\n 'recorder',\n null,\n $this->roleRepository,\n $this->roleChangeLogger,\n $this->updateUserRolesAction\n );\n\n $result = $mutator->handleDetail();\n\n $this->assertTrue($result);\n }\n\n private function createRole(string $name, int $id): Role\n {\n $role = new Role();\n $role->setAttribute('name', $name);\n $role->setAttribute('id', $id);\n\n return $role;\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny_jupiter","depth":4,"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":"19","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"19","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}]...
|
5407889838137062247
|
-8456102928079281755
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
<?php
declare(strict_types=1);
namespace Tests\Unit\Component\SCIM\Mutators\Attributes\User;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Jiminny\Actions\UpdateUserRolesAction;
use Jiminny\Component\Acl\RoleChangeLogger;
use Jiminny\Component\SCIM\Mutators\Attributes\User\RoleAttr;
use Jiminny\Contracts\Acl\RoleRepositoryInterface;
use Jiminny\DTO\Roles\UserRolesSyncDTO;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Role;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use PHPUnit\Framework\TestCase;
use Illuminate\Support\Collection;
final class RoleAttrTest extends TestCase
{
private RoleRepositoryInterface&\PHPUnit\Framework\MockObject\MockObject $roleRepository;
private RoleChangeLogger&\PHPUnit\Framework\MockObject\MockObject $roleChangeLogger;
private UpdateUserRolesAction&\PHPUnit\Framework\MockObject\MockObject $updateUserRolesAction;
private User&\PHPUnit\Framework\MockObject\MockObject $user;
private Team&\PHPUnit\Framework\MockObject\MockObject $team;
protected function setUp(): void
{
$this->roleRepository = $this->createMock(RoleRepositoryInterface::class);
$this->roleChangeLogger = $this->createMock(RoleChangeLogger::class);
$this->updateUserRolesAction = $this->createMock(UpdateUserRolesAction::class);
$this->user = $this->createMock(User::class);
$this->team = $this->createMock(Team::class);
}
public function testDoModificationWithReplaceAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute')
->with($this->callback(function (UserRolesSyncDTO $dto) {
return $dto->user === $this->user
&& $dto->roles->count() === 2;
}));
$mutator = new RoleAttr(
$this->user,
'recorder,analyst',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithAddAssignsRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsAdd(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithRemoveRemovesRoles(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$currentRoles = Collection::make([
$this->createRole('recorder', 1),
$this->createRole('analyst', 2),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->user->method('roles')->willReturn($currentRoles);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->once())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsRemove(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithInvalidRoleLogsWarning(): void
{
$roles = Collection::make([
$this->createRole('recorder', 1),
]);
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
Log::shouldReceive('warning')->once();
$mutator = new RoleAttr(
$this->user,
'invalid_role',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testDoModificationWithEmptyRolesDoesNothing(): void
{
$roles = Collection::make();
$this->user->method('getTeam')->willReturn($this->team);
$this->user->method('getId')->willReturn(123);
$this->team->method('getId')->willReturn(456);
$this->roleRepository->method('findAllByTeam')->with(456)->willReturn($roles);
$this->updateUserRolesAction->expects($this->never())
->method('execute');
$mutator = new RoleAttr(
$this->user,
'',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$mutator->setIsReplace(true);
$result = $mutator->doModification();
$this->assertSame($this->user, $result);
}
public function testHandleDetailReturnsTrue(): void
{
$mutator = new RoleAttr(
$this->user,
'recorder',
null,
$this->roleRepository,
$this->roleChangeLogger,
$this->updateUserRolesAction
);
$result = $mutator->handleDetail();
$this->assertTrue($result);
}
private function createRole(string $name, int $id): Role
{
$role = new Role();
$role->setAttribute('name', $name);
$role->setAttribute('id', $id);
return $role;
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny_jupiter
Sync Changes
Hide This Notification
Code changed:
Hide
19
19
17
Previous Highlighted Error
Next Highlighted Error...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53114
|
1863
|
3
|
2026-05-18T11:41:06.904772+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104466904_m2.jpg...
|
PhpStorm
|
faVsco.js – RoleAttrTest.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
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":"master, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.040226065,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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.8081782,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"bounds":{"left":0.8234708,"top":0.019952115,"width":0.09208777,"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 'AskJiminnyReportActivityServiceTest'","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 'AskJiminnyReportActivityServiceTest'","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}]...
|
8101791824998039176
|
-8348547798392525882
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
PhostormVIewINavicarecodeLaravelKeractorFV faVsco.js°9 master kProiectProphet>@ ProphetAiv DSCIMv D MutatorsC CoreUserRequest.phpscimProvistoning.ong© CoreUser.phpc) RoleAttrTest.php ?C 1extwessagingservice.phpV _ AtributesAccept RenectV D UserC) ActiveAttrlest.php° RoleAttrTest.php© ScimProvisioningTest.phpdeclare(strict types=1)"namesoace lests Unit comoonent Sol rutators Attrzoutes User:>W SettinasSidekick› use ..•D Slack7 Teaminsiahts21 DFinal class RoleAttrest extends TestcaseTranscriotionTranscriotionSummary12 usadecm Twilloprivate RoleRepositoryInterface&\PHPUnit\Framework \Mock0bject \Mock0bject SroleRepository:N Uoloade'D UtilityTM Waveformprivate RoleChangeLogger&\PHPUnit\Framework \Mock0bject\Mock0bject SroleChangeLogger:TN Wehhookc12 usages24 usagesprivate UpdateUserRolesAction&\PHPUnit\Framework \Mock0bject \Mock0bject SupdateUserRolesAction;D ConfigurationiM Consoleprivate User& PHPUnit\Framework\Mock0bject Mock0bject Suser:M Contractsorivace leanx rhrunit rranework nockudeст мосkud ect sceanD DTOm Enums29 @ >protected function setUp: voidf...}C Events38 D >exceptions0) fixturesMcuarde73 D >pubLic tunction testboMod1t1cat1onw1thkeplaceAss1qnskoleso: vo1d ...-Accept File & X Reject File 0x@+ 8 of 8 files →public function testDoModificationWithAddAssiqnsRoles: void{...}Local ChangesLog x• Chandes & tlles+→ E Side-by-side viewer y= env.local apo@cb4ebf0c config/logging.php• Constants.php app/Component/SCIM(C) CoreUser.oho aoo/DTO/SCIMIAAD/Resoonse@ CoreUserRequest.pho app/DTO/SCIMIAAD/Request(C)JiminnvDebuaCommand.oho apo/Console/Commands'driver' => 'errorlog''level' => env('LOG _LEVEL', 'info'),'path' => storage_path('logs/Laravel.log').pho logdina.nho confidC) ScimProvisioning.ohnann/comnonent/sclM@) SoftPhoneManager.nhnann/Comnonent/Twilio/Conference/ConferenceManad(C) TeytMessaainaService.nhn ann/Services/TelenhonviUnversioned Files 12 files= env nikilocal annl= .env.other app© CanAccessAiReportsTest.php tests/Unit/Policies(e) CrostoMockAck liminnvDonartDocultCommand.nhnann/Concolo/Commandc/pF0 favicon ico nublid=ids.txt appListenerRoleCannotHaveAdminOrManagerPermissionRule.php app/Rules0 ListenerRoleCannotHaveAdminOrManagerPermissionRuleTest.php tests/Unit/RP row cal auory cal annWorkcnace accociated with hranch 'macter' hac heon rectored Il Pollback II Confiaure (21 minutes aao)=custom.log= laravel.log4 SF jiminny@localhost]4 HS_local [jiminny@localhost]A console [STAGING] X556557* 558560•561|-562565567568569570575577579Tx: Auto vTe cri CouT LuuraqOnuE TanU UE ULaVUUUK LUE 1D:select * from teams:SELECT r.* FROM automated_reports rjoin teams t on r.team.id = t.idWHERE r.freguency = 'daily'and r.status = 1AND t.status = 'active'AND (r.expires_at >= now OR r.expires at IS NULL):select * from automated_report_results where report id IN (18, 33):select * from users where team_id = 1 and id = 1047:SELECT * FROM social_accounts WHERE sociable_id = 1047;|select x Tron aculvity searches where 10 = 10752select * from activity search_ filters where activity search_id = 10932:select * tromorder by 10 descorder by id desc;automated reports where 10 IN (555* from automated report results where id IN (81):select * trom users where 1d IN 00055,15787,11985071select * trom users where qroup 10 IN (5/00:SELECT * FROM automated_reports WHERE uuid_to_bin('18a06a75-afd2-476f-aadc-14d4057bdda2') = uuid;SELECT * FROMIautomated_report_results WHERE uuid_to_bin( 582d4b50-8cd3-42a9-9819-d676ff8f3b43) = uuid;select * from teams:"suppont Dally • In 15 m100% 2• Mon 18 May 14:41:06AskJiminnyReportActivityServiceTest v& console [PROD]# console [euyCascadeImplement Trial OwneFixing Redis Rate LimSalestorce Token Falli• SClM Role Manager+0 ..Go jiminny_jupiter• CoreUser.php019 A19 V17 ^ V8/10 tasks done• Update CoreUser response to include roles(9) Write unit tests for SCIM role management• ListenerRoleCannotHave)C PoleAttrTect nhiO oho artisan test --filter ListenerRoleCannotHaveAdmin0rManagerPermissionRuletestAsk anvthina (%4L)+ @code SWE-1.6RunX+ SkipReject allAccept all1ditterenceCurrent vercion'driver' => 'errorlog','level' => env('LOG_LEVEL', 'info'),'path' => storage_path( 'logs/Laravel.log')'custom channel' => ['driver' => 'single','path' => storage_path('loqs/custom.10g')'level' => env('LOG LEVEL', 'info').WN Windsurf Toams 1-1UTF.8io 4 spaces ©...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
53115
|
1862
|
3
|
2026-05-18T11:41:07.653556+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-18/1779 /Users/lukas/.screenpipe/data/data/2026-05-18/1779104467653_m1.jpg...
|
PhpStorm
|
faVsco.js – RoleAttrTest.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'...
|
[{"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":"master, menu","depth":5,"on_screen":true,"help_text":"Git Branch: master<br/>114 incoming commits<br/>","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":"AskJiminnyReportActivityServiceTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-2796147080355312063
|
-8636637127315567163
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
master, menu
Start Listen Project: faVsco.js, menu
master, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
SlackFileEditViewGoHistoryWindowHelpEU (DOCKER81DEV (docker)₴82DOCKER (docker-compose)asticsearch", "data"],"message": "No livingconnections"}1 {"type" : "log","@timestamp" : "2026-05-18T11:40:55Z","tags" : ["error","plug,"taskManager""taskManager"], "pid" :7, "message" : "Failed to pollfor work: Error: NoLiving connections"}kibanaticsearch"{"type": "log""elas,"data"],,"@timestamp"'2026-05-18T11:40:57Z'tags": ["error""pid" :7, "message" : "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch:9200"}kibana{"type" : "log""@timestamp": "2026-05-18T11:40:58Z","tags" : ["warning", "elasticsearch","data"], "pid" :7, "message" : "Unabletorevive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:40:582"asticsearch", "data"],'"pid":7, "message": "No livingconnections"},"tags" : ["warning", "elkibana1 {"type" : "log","@timestamp" : "2026-05-18T11:40:58Z", "tags" : ["error", "plugins""taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLivingconnections"}kibana1 {"type" : "log", "@timestamp" : "2026-05-18T11:40:59Z", "tags" : ["error", "elasticsearch""data"],"pid":7, "message" :"[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch: 9200"}kibana1 {"type" : "log","@timestamp": "2026-05-18T11:41:01Z", "tags" : ["warning", "elasticsearch", "data"], "pid" :7,"message": "Unable to revive connection: [URL_WITH_CREDENTIALS] : "2026-05-18T11:41:01Z","tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message"• "No livingconnections"}kibana1 {"type": "log""@timestamp": "2026-05-18T11:41:01Z""tags" : ["error", "plugins",, "taskManager""taskManager"], "pid":7, "message":"Failed to pollfor work: Error: NoLiving connections"}kibana{"type" : "log"ticsearch","@timestamp": "2026-05-18T11:41:02Z", "tags" : ["error","elas,"data"],"pid":7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearchelasticsearch: 9200"}docker_1amp_1kibana{"type" : "log", "@timestamp": "2026-05-18T11:41:04Z""tags" : ["warning", "elasticsearch", "data"], "pid" :7, "message": "Unable to revive connection: [URL_WITH_CREDENTIALS] "2026-05-18T11:41:04Z", "tags" : ["warning", "elasticsearch", "data"], "pid":7, "message": "No livingconnections"}kibanains"I {"type" : "log", "@timestamp" : "2026-05-18T11:41:047""tags" : ["error","plug,"taskManager""taskManager"], "pid":7, "message": "Failed to poll for work: Error: NoLiving connections"}kibana1 {"type": "log", "@timestamp": "2026-05-18T11:41:04Z", "tags" : ["error", "elasticsearch", "data"],"pid" :7, "message": "[ConnectionError]: getaddrinfo ENOTFOUND elasticsearch elasticsearch:9200"}View in Docker Desktop• View ConfigEnable Watch8• Mon 18 May 14:41:07Jiminny ...Metrotrtencee# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of_jimi...0 Direct messages. Nikolay YankovR. Aneliya Angelovaão Stefka Stoyanova. Stoyan Tomov€. Vasil VasilevGalya DimitrovaLe Todor Stamatov "N. Mario Georgiev. Nikolay IvanovLo James Graham *8 Stoyan Tanev&. Steliyan Georgiev&e Petko KashinskiE. Lukas Kovalik y...AppsJira CloudToastSupport Daily • in 19 m100% <7Describe what you are looking forNikolay YankovMessagesAdd canvas• Add language |TodayCONNECT/SYNC SETTINGSImport Email Conversations'OLet's Get Started:6 0O Files+HomeDMsActivityFilesLaterMoreSign in with Googleне ме пускаNikolay Yankov 2:17 PMЕй сегаГОТОВОLukas Kovalik 2:26 PMоще ведньж и в СФNikolay Yankov 2:27 PMГТОТОВОLukas Kovalik 2:27 PMработи вечеNikolay Yankov 2:27 PMимаше ли нещо за фиксLukas Kovalik 2:28 PMтрябва да си вързан към SF да работиNikolay Yankov 2:28 PMaxaaaMessage Nikolay Yankov...
|
53113
|
NULL
|
NULL
|
NULL
|