Pull groups from WhatsApp

This commit is contained in:
2026-02-18 21:22:45 +00:00
parent 521692c458
commit c400c46e7d
12 changed files with 643 additions and 136 deletions

1
.gitignore vendored
View File

@@ -163,3 +163,4 @@ static/
auth_debug.log auth_debug.log
genv genv
oom oom
node_modules/

View File

@@ -0,0 +1,89 @@
name route view response_kind templates
signup accounts/signup/ base.Signup html registration/registration_closed.html
notifications_update notifications/<str:type>/update/ notifications.NotificationsUpdate unknown -
system_settings settings/system/ system.SystemSettings html -
signal services/signal/ signal.Signal html -
whatsapp services/whatsapp/ whatsapp.WhatsApp html partials/signal-accounts.html
instagram services/instagram/ instagram.Instagram unknown -
signal_accounts services/signal/<str:type>/ signal.SignalAccounts unknown -
whatsapp_accounts services/whatsapp/<str:type>/ whatsapp.WhatsAppAccounts unknown -
instagram_accounts services/instagram/<str:type>/ instagram.InstagramAccounts unknown -
signal_contacts services/signal/<str:type>/contacts/<str:pk>/ signal.SignalContactsList unknown -
whatsapp_contacts services/whatsapp/<str:type>/contacts/<str:pk>/ whatsapp.WhatsAppContactsList unknown -
signal_chats services/signal/<str:type>/chats/<str:pk>/ signal.SignalChatsList unknown -
whatsapp_chats services/whatsapp/<str:type>/chats/<str:pk>/ whatsapp.WhatsAppChatsList unknown -
signal_messages services/signal/<str:type>/messages/<str:pk>/<str:chat_id>/ signal.SignalMessagesList unknown -
signal_account_add services/signal/<str:type>/add/ signal.SignalAccountAdd unknown -
whatsapp_account_add services/whatsapp/<str:type>/add/ whatsapp.WhatsAppAccountAdd html -
whatsapp_account_unlink services/whatsapp/<str:type>/unlink/<path:account>/ whatsapp.WhatsAppAccountUnlink html partials/signal-accounts.html
instagram_account_add services/instagram/<str:type>/add/ instagram.InstagramAccountAdd unknown -
compose_page compose/page/ compose.ComposePage html -
compose_workspace compose/workspace/ compose.ComposeWorkspace html -
compose_widget compose/widget/ compose.ComposeWidget html mixins/wm/widget.html
compose_workspace_contacts_widget compose/workspace/widget/contacts/ compose.ComposeWorkspaceContactsWidget html mixins/wm/widget.html
compose_send compose/send/ compose.ComposeSend html partials/compose-send-status.html
compose_cancel_send compose/cancel-send/ compose.ComposeCancelSend json -
compose_command_result compose/command-result/ compose.ComposeCommandResult html partials/compose-send-status.html
compose_drafts compose/drafts/ compose.ComposeDrafts json -
compose_summary compose/summary/ compose.ComposeSummary json -
compose_quick_insights compose/quick-insights/ compose.ComposeQuickInsights json -
compose_engage_preview compose/engage/preview/ compose.ComposeEngagePreview json -
compose_engage_send compose/engage/send/ compose.ComposeEngageSend json -
compose_thread compose/thread/ compose.ComposeThread json -
compose_history_sync compose/history-sync/ compose.ComposeHistorySync json -
compose_media_blob compose/media/blob/ compose.ComposeMediaBlob http -
compose_contacts_dropdown compose/widget/contacts/ compose.ComposeContactsDropdown html partials/nav-contacts-dropdown.html
compose_contact_match compose/contacts/match/ compose.ComposeContactMatch html -
ai_workspace ai/workspace/ workspace.AIWorkspace html -
ai_workspace_contacts ai/workspace/<str:type>/contacts/ workspace.AIWorkspaceContactsWidget html mixins/wm/widget.html
ai_workspace_person ai/workspace/<str:type>/person/<uuid:person_id>/ workspace.AIWorkspacePersonWidget html mixins/wm/widget.html
ai_workspace_person_timeline ai/workspace/<str:type>/person/<uuid:person_id>/timeline/ workspace.AIWorkspacePersonTimelineWidget html mixins/wm/widget.html
ai_workspace_insight_graphs ai/workspace/<str:type>/person/<uuid:person_id>/insights/graphs/ workspace.AIWorkspaceInsightGraphs html pages/ai-workspace-insight-graphs.html
ai_workspace_information ai/workspace/<str:type>/person/<uuid:person_id>/information/ workspace.AIWorkspaceInformation html pages/ai-workspace-information.html
ai_workspace_insight_help ai/workspace/<str:type>/person/<uuid:person_id>/insights/help/ workspace.AIWorkspaceInsightHelp html pages/ai-workspace-insight-help.html
ai_workspace_insight_detail ai/workspace/<str:type>/person/<uuid:person_id>/insights/<str:metric>/ workspace.AIWorkspaceInsightDetail html pages/ai-workspace-insight-detail.html
ai_workspace_run ai/workspace/<str:type>/person/<uuid:person_id>/run/<str:operation>/ workspace.AIWorkspaceRunOperation html partials/ai-workspace-ai-result.html
ai_workspace_send ai/workspace/<str:type>/person/<uuid:person_id>/send/ workspace.AIWorkspaceSendDraft unknown -
ai_workspace_queue ai/workspace/<str:type>/person/<uuid:person_id>/queue/ workspace.AIWorkspaceQueueDraft unknown -
ai_workspace_mitigation_create ai/workspace/<str:type>/person/<uuid:person_id>/mitigation/create/ workspace.AIWorkspaceCreateMitigation html partials/ai-workspace-mitigation-panel.html
ais ai/<str:type>/ ais.AIList unknown -
osint_search search/<str:type>/ osint.OSINTSearch html -
osint_workspace osint/workspace/ osint.OSINTWorkspace html -
osint_workspace_tabs_widget osint/workspace/widget/tabs/ osint.OSINTWorkspaceTabsWidget html mixins/wm/widget.html
ai_create ai/<str:type>/create/ ais.AICreate unknown -
ai_update ai/<str:type>/update/<str:pk>/ ais.AIUpdate unknown -
ai_delete ai/<str:type>/delete/<str:pk>/ ais.AIDelete unknown -
people person/<str:type>/ people.PersonList unknown -
person_create person/<str:type>/create/ people.PersonCreate unknown -
person_update person/<str:type>/update/<str:pk>/ people.PersonUpdate unknown -
person_delete person/<str:type>/delete/<str:pk>/ people.PersonDelete unknown -
groups group/<str:type>/ groups.GroupList unknown -
group_create group/<str:type>/create/ groups.GroupCreate unknown -
group_update group/<str:type>/update/<str:pk>/ groups.GroupUpdate unknown -
group_delete group/<str:type>/delete/<str:pk>/ groups.GroupDelete unknown -
personas persona/<str:type>/ personas.PersonaList unknown -
persona_create persona/<str:type>/create/ personas.PersonaCreate unknown -
persona_update persona/<str:type>/update/<str:pk>/ personas.PersonaUpdate unknown -
persona_delete persona/<str:type>/delete/<str:pk>/ personas.PersonaDelete unknown -
manipulations manipulation/<str:type>/ manipulations.ManipulationList unknown -
manipulation_create manipulation/<str:type>/create/ manipulations.ManipulationCreate unknown -
manipulation_update manipulation/<str:type>/update/<str:pk>/ manipulations.ManipulationUpdate unknown -
manipulation_delete manipulation/<str:type>/delete/<str:pk>/ manipulations.ManipulationDelete unknown -
sessions session/<str:type>/ sessions.SessionList unknown -
session_create session/<str:type>/create/ sessions.SessionCreate unknown -
session_update session/<str:type>/update/<str:pk>/ sessions.SessionUpdate unknown -
session_delete session/<str:type>/delete/<str:pk>/ sessions.SessionDelete unknown -
person_identifiers person/<str:type>/identifiers/<str:person>/ identifiers.PersonIdentifierList unknown -
person_identifier_create person/<str:type>/identifiers/create/<str:person> identifiers.PersonIdentifierCreate unknown -
person_identifier_update person/<str:type>/identifiers/update/<str:person>/<str:pk>/ identifiers.PersonIdentifierUpdate unknown -
person_identifier_delete person/<str:type>/identifiers/delete/<str:person>/<str:pk>/ identifiers.PersonIdentifierDelete unknown -
messages session/<str:type>/messages/<str:session>/ messages.MessageList unknown -
message_create session/<str:type>/messages/create/<str:session> messages.MessageCreate unknown -
message_update session/<str:type>/messages/update/<str:session>/<str:pk>/ messages.MessageUpdate unknown -
message_delete session/<str:type>/messages/delete/<str:session>/<str:pk>/ messages.MessageDelete unknown -
message_accept_api api/v1/queue/message/accept/<str:message_id>/ queues.AcceptMessageAPI http -
message_reject_api api/v1/queue/message/reject/<str:message_id>/ queues.RejectMessageAPI http -
queues queue/<str:type>/ queues.QueueList unknown -
queue_create queue/<str:type>/create/ queues.QueueCreate unknown -
queue_update queue/<str:type>/update/<str:pk>/ queues.QueueUpdate unknown -
queue_delete queue/<str:type>/delete/<str:pk>/ queues.QueueDelete unknown -
1 name route view response_kind templates
2 signup accounts/signup/ base.Signup html registration/registration_closed.html
3 notifications_update notifications/<str:type>/update/ notifications.NotificationsUpdate unknown -
4 system_settings settings/system/ system.SystemSettings html -
5 signal services/signal/ signal.Signal html -
6 whatsapp services/whatsapp/ whatsapp.WhatsApp html partials/signal-accounts.html
7 instagram services/instagram/ instagram.Instagram unknown -
8 signal_accounts services/signal/<str:type>/ signal.SignalAccounts unknown -
9 whatsapp_accounts services/whatsapp/<str:type>/ whatsapp.WhatsAppAccounts unknown -
10 instagram_accounts services/instagram/<str:type>/ instagram.InstagramAccounts unknown -
11 signal_contacts services/signal/<str:type>/contacts/<str:pk>/ signal.SignalContactsList unknown -
12 whatsapp_contacts services/whatsapp/<str:type>/contacts/<str:pk>/ whatsapp.WhatsAppContactsList unknown -
13 signal_chats services/signal/<str:type>/chats/<str:pk>/ signal.SignalChatsList unknown -
14 whatsapp_chats services/whatsapp/<str:type>/chats/<str:pk>/ whatsapp.WhatsAppChatsList unknown -
15 signal_messages services/signal/<str:type>/messages/<str:pk>/<str:chat_id>/ signal.SignalMessagesList unknown -
16 signal_account_add services/signal/<str:type>/add/ signal.SignalAccountAdd unknown -
17 whatsapp_account_add services/whatsapp/<str:type>/add/ whatsapp.WhatsAppAccountAdd html -
18 whatsapp_account_unlink services/whatsapp/<str:type>/unlink/<path:account>/ whatsapp.WhatsAppAccountUnlink html partials/signal-accounts.html
19 instagram_account_add services/instagram/<str:type>/add/ instagram.InstagramAccountAdd unknown -
20 compose_page compose/page/ compose.ComposePage html -
21 compose_workspace compose/workspace/ compose.ComposeWorkspace html -
22 compose_widget compose/widget/ compose.ComposeWidget html mixins/wm/widget.html
23 compose_workspace_contacts_widget compose/workspace/widget/contacts/ compose.ComposeWorkspaceContactsWidget html mixins/wm/widget.html
24 compose_send compose/send/ compose.ComposeSend html partials/compose-send-status.html
25 compose_cancel_send compose/cancel-send/ compose.ComposeCancelSend json -
26 compose_command_result compose/command-result/ compose.ComposeCommandResult html partials/compose-send-status.html
27 compose_drafts compose/drafts/ compose.ComposeDrafts json -
28 compose_summary compose/summary/ compose.ComposeSummary json -
29 compose_quick_insights compose/quick-insights/ compose.ComposeQuickInsights json -
30 compose_engage_preview compose/engage/preview/ compose.ComposeEngagePreview json -
31 compose_engage_send compose/engage/send/ compose.ComposeEngageSend json -
32 compose_thread compose/thread/ compose.ComposeThread json -
33 compose_history_sync compose/history-sync/ compose.ComposeHistorySync json -
34 compose_media_blob compose/media/blob/ compose.ComposeMediaBlob http -
35 compose_contacts_dropdown compose/widget/contacts/ compose.ComposeContactsDropdown html partials/nav-contacts-dropdown.html
36 compose_contact_match compose/contacts/match/ compose.ComposeContactMatch html -
37 ai_workspace ai/workspace/ workspace.AIWorkspace html -
38 ai_workspace_contacts ai/workspace/<str:type>/contacts/ workspace.AIWorkspaceContactsWidget html mixins/wm/widget.html
39 ai_workspace_person ai/workspace/<str:type>/person/<uuid:person_id>/ workspace.AIWorkspacePersonWidget html mixins/wm/widget.html
40 ai_workspace_person_timeline ai/workspace/<str:type>/person/<uuid:person_id>/timeline/ workspace.AIWorkspacePersonTimelineWidget html mixins/wm/widget.html
41 ai_workspace_insight_graphs ai/workspace/<str:type>/person/<uuid:person_id>/insights/graphs/ workspace.AIWorkspaceInsightGraphs html pages/ai-workspace-insight-graphs.html
42 ai_workspace_information ai/workspace/<str:type>/person/<uuid:person_id>/information/ workspace.AIWorkspaceInformation html pages/ai-workspace-information.html
43 ai_workspace_insight_help ai/workspace/<str:type>/person/<uuid:person_id>/insights/help/ workspace.AIWorkspaceInsightHelp html pages/ai-workspace-insight-help.html
44 ai_workspace_insight_detail ai/workspace/<str:type>/person/<uuid:person_id>/insights/<str:metric>/ workspace.AIWorkspaceInsightDetail html pages/ai-workspace-insight-detail.html
45 ai_workspace_run ai/workspace/<str:type>/person/<uuid:person_id>/run/<str:operation>/ workspace.AIWorkspaceRunOperation html partials/ai-workspace-ai-result.html
46 ai_workspace_send ai/workspace/<str:type>/person/<uuid:person_id>/send/ workspace.AIWorkspaceSendDraft unknown -
47 ai_workspace_queue ai/workspace/<str:type>/person/<uuid:person_id>/queue/ workspace.AIWorkspaceQueueDraft unknown -
48 ai_workspace_mitigation_create ai/workspace/<str:type>/person/<uuid:person_id>/mitigation/create/ workspace.AIWorkspaceCreateMitigation html partials/ai-workspace-mitigation-panel.html
49 ais ai/<str:type>/ ais.AIList unknown -
50 osint_search search/<str:type>/ osint.OSINTSearch html -
51 osint_workspace osint/workspace/ osint.OSINTWorkspace html -
52 osint_workspace_tabs_widget osint/workspace/widget/tabs/ osint.OSINTWorkspaceTabsWidget html mixins/wm/widget.html
53 ai_create ai/<str:type>/create/ ais.AICreate unknown -
54 ai_update ai/<str:type>/update/<str:pk>/ ais.AIUpdate unknown -
55 ai_delete ai/<str:type>/delete/<str:pk>/ ais.AIDelete unknown -
56 people person/<str:type>/ people.PersonList unknown -
57 person_create person/<str:type>/create/ people.PersonCreate unknown -
58 person_update person/<str:type>/update/<str:pk>/ people.PersonUpdate unknown -
59 person_delete person/<str:type>/delete/<str:pk>/ people.PersonDelete unknown -
60 groups group/<str:type>/ groups.GroupList unknown -
61 group_create group/<str:type>/create/ groups.GroupCreate unknown -
62 group_update group/<str:type>/update/<str:pk>/ groups.GroupUpdate unknown -
63 group_delete group/<str:type>/delete/<str:pk>/ groups.GroupDelete unknown -
64 personas persona/<str:type>/ personas.PersonaList unknown -
65 persona_create persona/<str:type>/create/ personas.PersonaCreate unknown -
66 persona_update persona/<str:type>/update/<str:pk>/ personas.PersonaUpdate unknown -
67 persona_delete persona/<str:type>/delete/<str:pk>/ personas.PersonaDelete unknown -
68 manipulations manipulation/<str:type>/ manipulations.ManipulationList unknown -
69 manipulation_create manipulation/<str:type>/create/ manipulations.ManipulationCreate unknown -
70 manipulation_update manipulation/<str:type>/update/<str:pk>/ manipulations.ManipulationUpdate unknown -
71 manipulation_delete manipulation/<str:type>/delete/<str:pk>/ manipulations.ManipulationDelete unknown -
72 sessions session/<str:type>/ sessions.SessionList unknown -
73 session_create session/<str:type>/create/ sessions.SessionCreate unknown -
74 session_update session/<str:type>/update/<str:pk>/ sessions.SessionUpdate unknown -
75 session_delete session/<str:type>/delete/<str:pk>/ sessions.SessionDelete unknown -
76 person_identifiers person/<str:type>/identifiers/<str:person>/ identifiers.PersonIdentifierList unknown -
77 person_identifier_create person/<str:type>/identifiers/create/<str:person> identifiers.PersonIdentifierCreate unknown -
78 person_identifier_update person/<str:type>/identifiers/update/<str:person>/<str:pk>/ identifiers.PersonIdentifierUpdate unknown -
79 person_identifier_delete person/<str:type>/identifiers/delete/<str:person>/<str:pk>/ identifiers.PersonIdentifierDelete unknown -
80 messages session/<str:type>/messages/<str:session>/ messages.MessageList unknown -
81 message_create session/<str:type>/messages/create/<str:session> messages.MessageCreate unknown -
82 message_update session/<str:type>/messages/update/<str:session>/<str:pk>/ messages.MessageUpdate unknown -
83 message_delete session/<str:type>/messages/delete/<str:session>/<str:pk>/ messages.MessageDelete unknown -
84 message_accept_api api/v1/queue/message/accept/<str:message_id>/ queues.AcceptMessageAPI http -
85 message_reject_api api/v1/queue/message/reject/<str:message_id>/ queues.RejectMessageAPI http -
86 queues queue/<str:type>/ queues.QueueList unknown -
87 queue_create queue/<str:type>/create/ queues.QueueCreate unknown -
88 queue_update queue/<str:type>/update/<str:pk>/ queues.QueueUpdate unknown -
89 queue_delete queue/<str:type>/delete/<str:pk>/ queues.QueueDelete unknown -

View File

@@ -0,0 +1,121 @@
occurrences files hash sample_locations
10 10 03b0f9bc20964f2440461604aa0ffb6c6ee7c3cd core/templates/partials/ai-list.html:47 | core/templates/partials/identifier-list.html:47 | core/templates/partials/group-list.html:57 | core/templates/partials/manipulation-list.html:85 | core/templates/partials/message-list.html:77 | core/templates/partials/person-list.html:68 | core/templates/partials/persona-list.html:65 | core/templates/partials/session-list.html:66 | core/templates/partials/signal-accounts.html:37 | core/templates/partials/signal-chats-list.html:44
9 9 f0d371d430810ce9eae58adc2d12965161068d13 core/templates/partials/ai-list.html:46 | core/templates/partials/identifier-list.html:46 | core/templates/partials/group-list.html:56 | core/templates/partials/manipulation-list.html:84 | core/templates/partials/message-list.html:76 | core/templates/partials/person-list.html:67 | core/templates/partials/persona-list.html:64 | core/templates/partials/session-list.html:65 | core/templates/partials/signal-chats-list.html:43
9 9 cff5da77248dead32e9fdc06f8fe42cddca30a30 core/templates/partials/ai-list.html:8 | core/templates/partials/identifier-list.html:8 | core/templates/partials/group-list.html:8 | core/templates/partials/manipulation-list.html:8 | core/templates/partials/message-list.html:8 | core/templates/partials/person-list.html:8 | core/templates/partials/persona-list.html:8 | core/templates/partials/session-list.html:8 | core/templates/partials/signal-chats-list.html:6
9 9 3d3fd1673ee12c057f8054da11425c2c2dfeedb0 core/templates/partials/ai-list.html:7 | core/templates/partials/identifier-list.html:7 | core/templates/partials/group-list.html:7 | core/templates/partials/manipulation-list.html:7 | core/templates/partials/message-list.html:7 | core/templates/partials/person-list.html:7 | core/templates/partials/persona-list.html:7 | core/templates/partials/session-list.html:7 | core/templates/partials/signal-chats-list.html:5
9 9 15b35686e496e5401cc946aacf6e9ce6ae44aae8 core/templates/partials/ai-list.html:6 | core/templates/partials/identifier-list.html:6 | core/templates/partials/group-list.html:6 | core/templates/partials/manipulation-list.html:6 | core/templates/partials/message-list.html:6 | core/templates/partials/person-list.html:6 | core/templates/partials/persona-list.html:6 | core/templates/partials/session-list.html:6 | core/templates/partials/signal-chats-list.html:4
8 8 f1690409577850c0ce9239566578d49ad9925a1a core/templates/partials/ai-list.html:31 | core/templates/partials/identifier-list.html:31 | core/templates/partials/group-list.html:41 | core/templates/partials/manipulation-list.html:69 | core/templates/partials/message-list.html:61 | core/templates/partials/person-list.html:43 | core/templates/partials/persona-list.html:49 | core/templates/partials/session-list.html:41
8 8 bc0b56758f9920642c9f185536d6cbf22f9b3396 core/templates/partials/ai-list.html:32 | core/templates/partials/identifier-list.html:32 | core/templates/partials/group-list.html:42 | core/templates/partials/manipulation-list.html:70 | core/templates/partials/message-list.html:62 | core/templates/partials/person-list.html:44 | core/templates/partials/persona-list.html:50 | core/templates/partials/session-list.html:42
8 8 a7a050a861dfa0a6281229e0eee563c603ab379b core/templates/partials/ai-list.html:9 | core/templates/partials/identifier-list.html:9 | core/templates/partials/group-list.html:9 | core/templates/partials/manipulation-list.html:9 | core/templates/partials/message-list.html:9 | core/templates/partials/person-list.html:9 | core/templates/partials/persona-list.html:9 | core/templates/partials/session-list.html:9
8 8 5c3b1591c15273065fdd44dd7733191989ba06d3 core/templates/partials/ai-list.html:51 | core/templates/partials/identifier-list.html:51 | core/templates/partials/group-list.html:61 | core/templates/partials/manipulation-list.html:89 | core/templates/partials/message-list.html:81 | core/templates/partials/person-list.html:72 | core/templates/partials/persona-list.html:69 | core/templates/partials/session-list.html:70
8 8 5335dd928d6669e16e59528846f9a98102da4981 core/templates/partials/ai-list.html:49 | core/templates/partials/identifier-list.html:49 | core/templates/partials/group-list.html:59 | core/templates/partials/manipulation-list.html:87 | core/templates/partials/message-list.html:79 | core/templates/partials/person-list.html:70 | core/templates/partials/persona-list.html:67 | core/templates/partials/session-list.html:68
8 8 4fc780e792516038cf09edc3dd9448701ee26e1c core/templates/partials/ai-list.html:30 | core/templates/partials/identifier-list.html:30 | core/templates/partials/group-list.html:40 | core/templates/partials/manipulation-list.html:68 | core/templates/partials/message-list.html:60 | core/templates/partials/person-list.html:42 | core/templates/partials/persona-list.html:48 | core/templates/partials/session-list.html:40
8 8 48b2a53f87354bf9691c37af1c9be5f81cf1ce7c core/templates/partials/ai-list.html:48 | core/templates/partials/identifier-list.html:48 | core/templates/partials/group-list.html:58 | core/templates/partials/manipulation-list.html:86 | core/templates/partials/message-list.html:78 | core/templates/partials/person-list.html:69 | core/templates/partials/persona-list.html:66 | core/templates/partials/session-list.html:67
8 8 3b4bddcd4ff63b68b226b8ded1236ffcc4bfd40f core/templates/partials/ai-list.html:50 | core/templates/partials/identifier-list.html:50 | core/templates/partials/group-list.html:60 | core/templates/partials/manipulation-list.html:88 | core/templates/partials/message-list.html:80 | core/templates/partials/person-list.html:71 | core/templates/partials/persona-list.html:68 | core/templates/partials/session-list.html:69
8 8 2d766ff045ee8134a71845d7f58ffe06bc83cf5a core/templates/partials/ai-list.html:33 | core/templates/partials/identifier-list.html:33 | core/templates/partials/group-list.html:43 | core/templates/partials/manipulation-list.html:71 | core/templates/partials/message-list.html:63 | core/templates/partials/person-list.html:45 | core/templates/partials/persona-list.html:51 | core/templates/partials/session-list.html:43
8 8 07c707357a23b0e9a988243ec392f0551d8f5ae8 core/templates/partials/ai-list.html:29 | core/templates/partials/identifier-list.html:29 | core/templates/partials/group-list.html:39 | core/templates/partials/manipulation-list.html:67 | core/templates/partials/message-list.html:59 | core/templates/partials/person-list.html:41 | core/templates/partials/persona-list.html:47 | core/templates/partials/session-list.html:39
8 1 9abf8a2f282656184f30aa39a5691f1ba3c5f751 core/views/workspace.py:4435 | core/views/workspace.py:4532 | core/views/workspace.py:4533 | core/views/workspace.py:4784 | core/views/workspace.py:4785 | core/views/workspace.py:5049 | core/views/workspace.py:5119 | core/views/workspace.py:5143
6 6 fd8babf68e334229e8760b124291f08ba139e8da core/templates/partials/group-list.html:18 | core/templates/partials/manipulation-list.html:22 | core/templates/partials/message-list.html:24 | core/templates/partials/person-list.html:19 | core/templates/partials/persona-list.html:22 | core/templates/partials/session-list.html:18
6 6 f6142cb779dc039ab3c55f0cd88686d17b0bf715 core/templates/partials/group-list.html:22 | core/templates/partials/manipulation-list.html:26 | core/templates/partials/message-list.html:28 | core/templates/partials/person-list.html:23 | core/templates/partials/persona-list.html:26 | core/templates/partials/session-list.html:22
6 6 efeb0fa9dbc193eff23b464d67ca1fbf81d5bdbf core/templates/partials/group-list.html:25 | core/templates/partials/manipulation-list.html:29 | core/templates/partials/message-list.html:31 | core/templates/partials/person-list.html:26 | core/templates/partials/persona-list.html:29 | core/templates/partials/session-list.html:25
6 6 ea9c94038a099db8763e33d44239d2fc1ec791b5 core/templates/partials/group-list.html:19 | core/templates/partials/manipulation-list.html:23 | core/templates/partials/message-list.html:25 | core/templates/partials/person-list.html:20 | core/templates/partials/persona-list.html:23 | core/templates/partials/session-list.html:19
6 6 d34851ba01afdef301f4d95b748ac93567759315 core/templates/partials/group-list.html:21 | core/templates/partials/manipulation-list.html:25 | core/templates/partials/message-list.html:27 | core/templates/partials/person-list.html:22 | core/templates/partials/persona-list.html:25 | core/templates/partials/session-list.html:21
6 6 c198cfd05631c42f80c1cac33060bcae2a8314e1 core/templates/partials/group-list.html:24 | core/templates/partials/manipulation-list.html:28 | core/templates/partials/message-list.html:30 | core/templates/partials/person-list.html:25 | core/templates/partials/persona-list.html:28 | core/templates/partials/session-list.html:24
6 6 b7ffcc018c542592630ae4aa179df63fd73124c6 core/templates/partials/ai-list.html:35 | core/templates/partials/identifier-list.html:35 | core/templates/partials/group-list.html:45 | core/templates/partials/manipulation-list.html:73 | core/templates/partials/message-list.html:65 | core/templates/partials/persona-list.html:53
6 6 b0e7bdb0253104d309381cd1b92b8b1361011497 core/templates/partials/group-list.html:20 | core/templates/partials/manipulation-list.html:24 | core/templates/partials/message-list.html:26 | core/templates/partials/person-list.html:21 | core/templates/partials/persona-list.html:24 | core/templates/partials/session-list.html:20
6 6 642e4fd07d36ac1672d41a357245fe65d548a167 core/templates/partials/ai-list.html:34 | core/templates/partials/identifier-list.html:34 | core/templates/partials/group-list.html:44 | core/templates/partials/manipulation-list.html:72 | core/templates/partials/message-list.html:64 | core/templates/partials/persona-list.html:52
6 6 5a0aa6e4c55089cc2bc930f5b41e6460c03377be core/templates/partials/group-list.html:23 | core/templates/partials/manipulation-list.html:27 | core/templates/partials/message-list.html:29 | core/templates/partials/person-list.html:24 | core/templates/partials/persona-list.html:27 | core/templates/partials/session-list.html:23
6 3 d59a18a73345a4d9ee14cf53c0651e265283500f core/templates/two_factor/core/login.html:25 | core/templates/two_factor/core/login.html:26 | core/templates/two_factor/core/phone_register.html:15 | core/templates/two_factor/core/phone_register.html:16 | core/templates/two_factor/core/setup.html:47 | core/templates/two_factor/core/setup.html:48
6 1 f508ae701315bdaff81c2ed4fc909dcfe3063e12 core/views/workspace.py:4434 | core/views/workspace.py:4531 | core/views/workspace.py:4783 | core/views/workspace.py:5048 | core/views/workspace.py:5118 | core/views/workspace.py:5142
5 1 9e2147b111b0b4cfde1741ff031ed5eeee0fb90c core/views/workspace.py:3565 | core/views/workspace.py:3612 | core/views/workspace.py:3673 | core/views/workspace.py:3692 | core/views/workspace.py:3734
4 4 f1aa79edd9798ce0335811883d9edf5641caa560 core/views/groups.py:6 | core/views/manipulations.py:6 | core/views/people.py:6 | core/views/personas.py:6
4 4 f0ec5c6490de79592d8bc01b014f7dcccd0ef038 core/templates/partials/ai-list.html:44 | core/templates/partials/identifier-list.html:44 | core/templates/partials/message-list.html:74 | core/templates/partials/session-list.html:63
4 4 e8c131c539e745e9225a33c05a8eaa7373258d54 core/templates/pages/ai-workspace-insight-detail.html:4 | core/templates/pages/ai-workspace-insight-graphs.html:4 | core/templates/pages/ai-workspace-insight-help.html:3 | core/templates/pages/ai-workspace-information.html:3
4 4 decebca418375e5a29ab01c3754b762d2657233f core/templates/pages/ai-workspace-insight-detail.html:3 | core/templates/pages/ai-workspace-insight-graphs.html:3 | core/templates/pages/ai-workspace-insight-help.html:2 | core/templates/pages/ai-workspace-information.html:2
4 4 b7577e6b11ec4d0fa29bb51c1a83825ce02fd524 core/templates/partials/ai-list.html:42 | core/templates/partials/identifier-list.html:42 | core/templates/partials/message-list.html:72 | core/templates/partials/session-list.html:61
4 4 75767eb7e8ab76bbe8ef7adbdbcb3885d5f49928 core/templates/partials/ai-list.html:45 | core/templates/partials/identifier-list.html:45 | core/templates/partials/message-list.html:75 | core/templates/partials/session-list.html:64
4 4 1eb40cc61e3b91955ce88faa8fd0046ec6551929 core/templates/partials/ai-list.html:43 | core/templates/partials/identifier-list.html:43 | core/templates/partials/message-list.html:73 | core/templates/partials/session-list.html:62
4 2 cee0c018559f005cebe0b2d6b3b50dbaebc40d66 core/templates/pages/compose-contact-match.html:22 | core/templates/pages/compose-contact-match.html:23 | core/templates/pages/system-settings.html:8 | core/templates/pages/system-settings.html:9
4 2 c9cfa57fda2e778ee9b5eada947cbf891b6664e9 core/views/signal.py:53 | core/views/whatsapp.py:52 | core/views/whatsapp.py:93 | core/views/whatsapp.py:127
4 2 445d072fad4a4544aa92401013bc2a116fa85412 core/views/signal.py:54 | core/views/whatsapp.py:53 | core/views/whatsapp.py:94 | core/views/whatsapp.py:128
4 2 07396bab972b3c399891794476328d05274a7b24 core/views/signal.py:55 | core/views/whatsapp.py:54 | core/views/whatsapp.py:95 | core/views/whatsapp.py:129
4 1 fc8954f193dbb953ac30413e97ca67dcc1995196 core/views/whatsapp.py:214 | core/views/whatsapp.py:245 | core/views/whatsapp.py:311 | core/views/whatsapp.py:346
4 1 f6b88ff8738ebf9460faad2c3e174be762c67293 core/views/osint.py:147 | core/views/osint.py:205 | core/views/osint.py:244 | core/views/osint.py:322
4 1 e439fdd1e1150add02fabd3c9eba6b2042956e5b core/views/workspace.py:4581 | core/views/workspace.py:4635 | core/views/workspace.py:4710 | core/views/workspace.py:4749
4 1 dbcf5eed0e71ad85b4292e3046783ef1c09313e7 core/views/workspace.py:4582 | core/views/workspace.py:4636 | core/views/workspace.py:4711 | core/views/workspace.py:4750
4 1 c6fd3be26b4dac1b081caf2c23fe4017cb32c79d core/views/osint.py:151 | core/views/osint.py:209 | core/views/osint.py:248 | core/views/osint.py:326
4 1 619a8132534bf4cf49cb9025caefc4368d23027a core/views/workspace.py:3983 | core/views/workspace.py:4001 | core/views/workspace.py:4112 | core/views/workspace.py:4149
4 1 55794b46a1c1398e2854efcd5956b72457efafbc core/views/osint.py:150 | core/views/osint.py:208 | core/views/osint.py:247 | core/views/osint.py:325
4 1 51ed1c5494a45d890fab39751a78358c70a665d2 core/views/workspace.py:4583 | core/views/workspace.py:4637 | core/views/workspace.py:4712 | core/views/workspace.py:4751
4 1 362730b92c130e505e810721f050388d6dca84ea core/views/workspace.py:3566 | core/views/workspace.py:3674 | core/views/workspace.py:3693 | core/views/workspace.py:3735
4 1 28331d9d604508bf90644afe2bca3459260288d3 core/views/osint.py:148 | core/views/osint.py:206 | core/views/osint.py:245 | core/views/osint.py:323
4 1 24492ca0de0ef20daca922e99241c19e2b4476d9 core/views/compose.py:2829 | core/views/compose.py:2890 | core/views/compose.py:2953 | core/views/compose.py:3069
4 1 0429ccd645a7c781c66863de386fff245096acb8 core/templates/partials/signal-chats-list.html:55 | core/templates/partials/signal-chats-list.html:64 | core/templates/partials/signal-chats-list.html:100 | core/templates/partials/signal-chats-list.html:108
4 1 00402420dfa95d67fb1fa047200c2c7ebec9e09f core/views/osint.py:149 | core/views/osint.py:207 | core/views/osint.py:246 | core/views/osint.py:324
3 3 f72bfbb1fa3aaba54774035a28fa32d40d07782e core/templates/partials/whatsapp-contacts-list.html:188 | core/templates/pages/compose-contact-match.html:322 | core/templates/partials/osint/list-table.html:320
3 3 ef0d9a8dd3eba020022cc111353fac4dede707aa core/templates/partials/whatsapp-contacts-list.html:189 | core/templates/pages/compose-contact-match.html:323 | core/templates/partials/osint/list-table.html:321
3 3 e93f48a528a0b829c15e836f52b0688302294cd7 core/templates/mixins/window-content/persona-form.html:3 | core/templates/mixins/window-content/queue-form-inline.html:3 | core/templates/mixins/window-content/person-form.html:3
3 3 e78a723340a9cdd4c8342598dd77df9905695934 core/templates/partials/group-list.html:54 | core/templates/partials/manipulation-list.html:82 | core/templates/partials/person-list.html:65
3 3 d2066ceb8eb4188b1dda46b55168a971bab01fa8 core/templates/partials/group-list.html:55 | core/templates/partials/manipulation-list.html:83 | core/templates/partials/person-list.html:66
3 3 c28449d0cb2fbbd1ae9a2fcf1894e23fe691b28f core/templates/mixins/window-content/persona-form.html:120 | core/templates/mixins/window-content/queue-form-inline.html:36 | core/templates/mixins/window-content/person-form.html:127
3 3 b95648f6e16638f41aa87271ea707743183c6c07 core/templates/mixins/window-content/persona-form.html:35 | core/templates/mixins/window-content/queue-form-inline.html:20 | core/templates/mixins/window-content/person-form.html:41
3 3 9bf9b960c86ce18ec24c06722eae061ba6c53b0d core/templates/mixins/window-content/persona-form.html:33 | core/templates/mixins/window-content/queue-form-inline.html:18 | core/templates/mixins/window-content/person-form.html:39
3 3 90ecfd0cc6216452e3e1cf70802b6c5bf31bb6e1 core/templates/registration/login.html:1 | core/templates/registration/registration_closed.html:1 | core/templates/registration/signup.html:1
3 3 908efc2243afc21fdb03a03590b487053bd8df62 core/views/signal.py:27 | core/views/system.py:136 | core/views/whatsapp.py:23
3 3 8d46d1c156fa9b3f9ada7cfa9a2307e47cbd1d7d core/templates/mixins/window-content/persona-form.html:4 | core/templates/mixins/window-content/queue-form-inline.html:4 | core/templates/mixins/window-content/person-form.html:4
3 3 88de0e7ee40636f90fdc006951eecceab664b9d9 core/templates/registration/login.html:2 | core/templates/registration/registration_closed.html:2 | core/templates/registration/signup.html:2
3 3 8298593e6112ae5b1b79d784687dbe0adf81986c core/templates/mixins/window-content/persona-form.html:31 | core/templates/mixins/window-content/queue-form-inline.html:16 | core/templates/mixins/window-content/person-form.html:37
3 3 7faa673ea237e033b1c17cfedd0ea28aa663a47f core/templates/two_factor/core/login.html:24 | core/templates/two_factor/core/phone_register.html:14 | core/templates/two_factor/core/setup.html:46
3 3 70dd01d1657eb83f2e2683cb57ddb48c6c096461 core/templates/mixins/window-content/persona-form.html:2 | core/templates/mixins/window-content/queue-form-inline.html:2 | core/templates/mixins/window-content/person-form.html:2
3 3 57a7ed538e23b8b8fe2fe43f7de9a034ca5dc09e core/templates/registration/login.html:3 | core/templates/registration/registration_closed.html:3 | core/templates/registration/signup.html:3
3 3 46899ae970e49b3abf0ee240164475e500a508af core/templates/mixins/window-content/persona-form.html:32 | core/templates/mixins/window-content/queue-form-inline.html:17 | core/templates/mixins/window-content/person-form.html:38
3 3 39756b7a97a94b78848422a3db963aca123f793f core/templates/partials/whatsapp-contacts-list.html:163 | core/templates/pages/compose-contact-match.html:293 | core/templates/partials/osint/list-table.html:301
3 3 1c17b1165c7bdf1f43b33059f711f6f85b9cc218 core/templates/registration/login.html:4 | core/templates/registration/registration_closed.html:4 | core/templates/registration/signup.html:4
3 3 1be799390181879613097b249220ae621ff07cdb core/templates/partials/whatsapp-contacts-list.html:127 | core/templates/pages/compose-contact-match.html:447 | core/templates/partials/osint/list-table.html:261
3 3 19aab351002986a405384ecd3ce7608eb76d8bc5 core/templates/partials/group-list.html:53 | core/templates/partials/manipulation-list.html:81 | core/templates/partials/person-list.html:64
3 3 14aff65587a9bbd6eb7756ac5c8efd169b560a3a core/templates/partials/whatsapp-contacts-list.html:126 | core/templates/pages/compose-contact-match.html:446 | core/templates/partials/osint/list-table.html:260
3 3 14919fe1ff418d66f3d00bc460657f29d2fd8526 core/templates/partials/whatsapp-contacts-list.html:187 | core/templates/pages/compose-contact-match.html:321 | core/templates/partials/osint/list-table.html:319
3 3 0aca7b3de6e51826f65cb5b05b2384c26dfbbe46 core/templates/mixins/window-content/persona-form.html:34 | core/templates/mixins/window-content/queue-form-inline.html:19 | core/templates/mixins/window-content/person-form.html:40
3 3 0641ecc380ba0553c7878bf54bf39cb608f34fec core/templates/partials/group-list.html:52 | core/templates/partials/manipulation-list.html:80 | core/templates/partials/person-list.html:63
3 1 ff9e899aa632040f0c8c04c35c724a584e96170e core/views/workspace.py:4613 | core/views/workspace.py:4727 | core/views/workspace.py:4769
3 1 fd59c665fdc5e87a6d79967a146b4bdce2522e26 core/views/workspace.py:2277 | core/views/workspace.py:2278 | core/views/workspace.py:2279
3 1 e9480c8124135a1145d5b7f1adbad08d521efef4 core/views/whatsapp.py:66 | core/views/whatsapp.py:110 | core/views/whatsapp.py:142
3 1 e5fea1892e0910137b87aec081e0f617647d9e3c core/views/workspace.py:977 | core/views/workspace.py:978 | core/views/workspace.py:979
3 1 e28608b35e727724aecee511c796d18764cf672e core/templates/partials/compose-panel.html:126 | core/templates/partials/compose-panel.html:135 | core/templates/partials/compose-panel.html:144
3 1 dcd7eefc5742cbb8365f3f1f75183a7cc7615749 core/views/osint.py:155 | core/views/osint.py:213 | core/views/osint.py:330
3 1 d9b54261b181ce1f8c5072e7c8a1df8b3d001a77 core/views/workspace.py:3498 | core/views/workspace.py:3499 | core/views/workspace.py:3500
3 1 d975831bc528875ccfb5b7de5482552df7e943d5 core/views/compose.py:2861 | core/views/compose.py:2916 | core/views/compose.py:3077
3 1 d931b65a39674b3ee1691f50052d5e14b7df0c75 core/views/osint.py:146 | core/views/osint.py:204 | core/views/osint.py:321
3 1 d7908443bf8c1ca621dd346a0f86ef0bff940ab3 core/views/osint.py:156 | core/views/osint.py:214 | core/views/osint.py:331
3 1 d6b5f871e150b61a5e188e49f6dff02004193b3f core/views/compose.py:1001 | core/views/compose.py:1025 | core/views/compose.py:1351
3 1 d5c2ada1785825894266d98c57132b2544285032 core/views/compose.py:2830 | core/views/compose.py:2891 | core/views/compose.py:3070
3 1 d0b3c17ee7292bc9b72a6b225e7a1e60e9b205df core/views/compose.py:1002 | core/views/compose.py:1026 | core/views/compose.py:1352
3 1 cf1c4409875350836b698858dd25c6ecbde16919 core/views/compose.py:1000 | core/views/compose.py:1024 | core/views/compose.py:1350
3 1 c3d51fd612a0951656a5ecfa90f43e6d01f74397 core/views/signal.py:14 | core/views/signal.py:15 | core/views/signal.py:16
3 1 c07232d37f44c24fd72a425d5f3118b44ea878f4 core/views/workspace.py:3572 | core/views/workspace.py:3618 | core/views/workspace.py:4012
3 1 9c0c4d771d15fd5b044aaa34f60455b6a0162e41 core/views/workspace.py:4903 | core/views/workspace.py:4928 | core/views/workspace.py:4992
3 1 9b90da14fc1b8dbb3a77a0fe5e33ff8100ce8521 core/views/workspace.py:829 | core/views/workspace.py:830 | core/views/workspace.py:831
3 1 93f4f5c3e371c438ae0c43856c3828f6d117f72a core/views/compose.py:2831 | core/views/compose.py:2892 | core/views/compose.py:3071
3 1 90816cacf56a5067ae41905f8b55fe9d29537860 core/views/workspace.py:4612 | core/views/workspace.py:4726 | core/views/workspace.py:4768
3 1 8684b5111e67349fe997efcd70aafcea4fdf83e5 core/views/whatsapp.py:65 | core/views/whatsapp.py:109 | core/views/whatsapp.py:141
3 1 84ec68e536092f636e85f5fa8b8c62e132f8cf44 core/views/whatsapp.py:64 | core/views/whatsapp.py:108 | core/views/whatsapp.py:140
3 1 833c0a4b126b5483e2d9a371decbb6e64d8be31b core/views/osint.py:154 | core/views/osint.py:212 | core/views/osint.py:329
3 1 7fd26a8f251b682cc6532ea3e4b940e3c9d724f8 core/views/osint.py:158 | core/views/osint.py:216 | core/views/osint.py:333
3 1 6761ee7ea5444b7947203328998c87732446c26b core/views/osint.py:152 | core/views/osint.py:210 | core/views/osint.py:327
3 1 5d3621d8d1df3d692b03d7496a88f1983344f910 core/views/compose.py:997 | core/views/compose.py:1021 | core/views/compose.py:1347
3 1 5bf9dc3e0e46be35ae97b3d487cd626913f4917e core/views/instagram.py:3 | core/views/instagram.py:4 | core/views/instagram.py:5
3 1 59eb8ce9add8ac3ca30c2bd7910d926f4c1acf71 core/views/osint.py:153 | core/views/osint.py:211 | core/views/osint.py:328
3 1 595d7274dc3c2036cb637962dbdb26e5b72cd48d core/views/compose.py:2371 | core/views/compose.py:2580 | core/views/compose.py:2641
3 1 58d137eecf70203c332d9ac8eedf22185a5de4b3 core/views/workspace.py:3672 | core/views/workspace.py:3691 | core/views/workspace.py:3733
3 1 49cbc2c109a4ef6091f5fef70963ffbde0fa802d core/views/workspace.py:4164 | core/views/workspace.py:4243 | core/views/workspace.py:4300
3 1 481567b5ee2d425e4c04ce614ec8190ac6736a58 core/templates/partials/compose-panel.html:2667 | core/templates/partials/compose-panel.html:2729 | core/templates/partials/compose-panel.html:2751
3 1 42c0f869021d8ad0c15da6c4463dba6d2ec11643 core/views/compose.py:1003 | core/views/compose.py:1027 | core/views/compose.py:1353
3 1 421098a2efd474573beaccceeb159a576ee45114 core/views/workspace.py:4163 | core/views/workspace.py:4242 | core/views/workspace.py:4299
3 1 3ce072494ff8ce4a9e08df7924cb4038c02a0bc9 core/views/workspace.py:4614 | core/views/workspace.py:4728 | core/views/workspace.py:4770
3 1 3907486b4a80932f68c6b9354b3d9b0325c21d39 core/templates/partials/compose-panel.html:125 | core/templates/partials/compose-panel.html:134 | core/templates/partials/compose-panel.html:143
3 1 2c60e1087d82418be7362e9258da08450bc81c86 core/views/compose.py:2862 | core/views/compose.py:2917 | core/views/compose.py:3078
3 1 28720796d0312972c14eb707f03025ac3f574d16 core/views/osint.py:157 | core/views/osint.py:215 | core/views/osint.py:332
3 1 2492a12ee36707345d81b61907f4190db03c91cf core/views/workspace.py:1625 | core/views/workspace.py:1626 | core/views/workspace.py:1627
3 1 136f738cdbb963f2f131083bc6a4914baa489a99 core/templates/partials/ai-workspace-person-widget.html:581 | core/templates/partials/ai-workspace-person-widget.html:610 | core/templates/partials/ai-workspace-person-widget.html:647
3 1 112897fea978b734511faa475861bc87fb740ce4 core/views/compose.py:998 | core/views/compose.py:1022 | core/views/compose.py:1348
3 1 1112dde96781a149bcb3aa9da756f7f8a1d8cfc9 core/views/workspace.py:4520 | core/views/workspace.py:4559 | core/views/workspace.py:5033
1 occurrences files hash sample_locations
2 10 10 03b0f9bc20964f2440461604aa0ffb6c6ee7c3cd core/templates/partials/ai-list.html:47 | core/templates/partials/identifier-list.html:47 | core/templates/partials/group-list.html:57 | core/templates/partials/manipulation-list.html:85 | core/templates/partials/message-list.html:77 | core/templates/partials/person-list.html:68 | core/templates/partials/persona-list.html:65 | core/templates/partials/session-list.html:66 | core/templates/partials/signal-accounts.html:37 | core/templates/partials/signal-chats-list.html:44
3 9 9 f0d371d430810ce9eae58adc2d12965161068d13 core/templates/partials/ai-list.html:46 | core/templates/partials/identifier-list.html:46 | core/templates/partials/group-list.html:56 | core/templates/partials/manipulation-list.html:84 | core/templates/partials/message-list.html:76 | core/templates/partials/person-list.html:67 | core/templates/partials/persona-list.html:64 | core/templates/partials/session-list.html:65 | core/templates/partials/signal-chats-list.html:43
4 9 9 cff5da77248dead32e9fdc06f8fe42cddca30a30 core/templates/partials/ai-list.html:8 | core/templates/partials/identifier-list.html:8 | core/templates/partials/group-list.html:8 | core/templates/partials/manipulation-list.html:8 | core/templates/partials/message-list.html:8 | core/templates/partials/person-list.html:8 | core/templates/partials/persona-list.html:8 | core/templates/partials/session-list.html:8 | core/templates/partials/signal-chats-list.html:6
5 9 9 3d3fd1673ee12c057f8054da11425c2c2dfeedb0 core/templates/partials/ai-list.html:7 | core/templates/partials/identifier-list.html:7 | core/templates/partials/group-list.html:7 | core/templates/partials/manipulation-list.html:7 | core/templates/partials/message-list.html:7 | core/templates/partials/person-list.html:7 | core/templates/partials/persona-list.html:7 | core/templates/partials/session-list.html:7 | core/templates/partials/signal-chats-list.html:5
6 9 9 15b35686e496e5401cc946aacf6e9ce6ae44aae8 core/templates/partials/ai-list.html:6 | core/templates/partials/identifier-list.html:6 | core/templates/partials/group-list.html:6 | core/templates/partials/manipulation-list.html:6 | core/templates/partials/message-list.html:6 | core/templates/partials/person-list.html:6 | core/templates/partials/persona-list.html:6 | core/templates/partials/session-list.html:6 | core/templates/partials/signal-chats-list.html:4
7 8 8 f1690409577850c0ce9239566578d49ad9925a1a core/templates/partials/ai-list.html:31 | core/templates/partials/identifier-list.html:31 | core/templates/partials/group-list.html:41 | core/templates/partials/manipulation-list.html:69 | core/templates/partials/message-list.html:61 | core/templates/partials/person-list.html:43 | core/templates/partials/persona-list.html:49 | core/templates/partials/session-list.html:41
8 8 8 bc0b56758f9920642c9f185536d6cbf22f9b3396 core/templates/partials/ai-list.html:32 | core/templates/partials/identifier-list.html:32 | core/templates/partials/group-list.html:42 | core/templates/partials/manipulation-list.html:70 | core/templates/partials/message-list.html:62 | core/templates/partials/person-list.html:44 | core/templates/partials/persona-list.html:50 | core/templates/partials/session-list.html:42
9 8 8 a7a050a861dfa0a6281229e0eee563c603ab379b core/templates/partials/ai-list.html:9 | core/templates/partials/identifier-list.html:9 | core/templates/partials/group-list.html:9 | core/templates/partials/manipulation-list.html:9 | core/templates/partials/message-list.html:9 | core/templates/partials/person-list.html:9 | core/templates/partials/persona-list.html:9 | core/templates/partials/session-list.html:9
10 8 8 5c3b1591c15273065fdd44dd7733191989ba06d3 core/templates/partials/ai-list.html:51 | core/templates/partials/identifier-list.html:51 | core/templates/partials/group-list.html:61 | core/templates/partials/manipulation-list.html:89 | core/templates/partials/message-list.html:81 | core/templates/partials/person-list.html:72 | core/templates/partials/persona-list.html:69 | core/templates/partials/session-list.html:70
11 8 8 5335dd928d6669e16e59528846f9a98102da4981 core/templates/partials/ai-list.html:49 | core/templates/partials/identifier-list.html:49 | core/templates/partials/group-list.html:59 | core/templates/partials/manipulation-list.html:87 | core/templates/partials/message-list.html:79 | core/templates/partials/person-list.html:70 | core/templates/partials/persona-list.html:67 | core/templates/partials/session-list.html:68
12 8 8 4fc780e792516038cf09edc3dd9448701ee26e1c core/templates/partials/ai-list.html:30 | core/templates/partials/identifier-list.html:30 | core/templates/partials/group-list.html:40 | core/templates/partials/manipulation-list.html:68 | core/templates/partials/message-list.html:60 | core/templates/partials/person-list.html:42 | core/templates/partials/persona-list.html:48 | core/templates/partials/session-list.html:40
13 8 8 48b2a53f87354bf9691c37af1c9be5f81cf1ce7c core/templates/partials/ai-list.html:48 | core/templates/partials/identifier-list.html:48 | core/templates/partials/group-list.html:58 | core/templates/partials/manipulation-list.html:86 | core/templates/partials/message-list.html:78 | core/templates/partials/person-list.html:69 | core/templates/partials/persona-list.html:66 | core/templates/partials/session-list.html:67
14 8 8 3b4bddcd4ff63b68b226b8ded1236ffcc4bfd40f core/templates/partials/ai-list.html:50 | core/templates/partials/identifier-list.html:50 | core/templates/partials/group-list.html:60 | core/templates/partials/manipulation-list.html:88 | core/templates/partials/message-list.html:80 | core/templates/partials/person-list.html:71 | core/templates/partials/persona-list.html:68 | core/templates/partials/session-list.html:69
15 8 8 2d766ff045ee8134a71845d7f58ffe06bc83cf5a core/templates/partials/ai-list.html:33 | core/templates/partials/identifier-list.html:33 | core/templates/partials/group-list.html:43 | core/templates/partials/manipulation-list.html:71 | core/templates/partials/message-list.html:63 | core/templates/partials/person-list.html:45 | core/templates/partials/persona-list.html:51 | core/templates/partials/session-list.html:43
16 8 8 07c707357a23b0e9a988243ec392f0551d8f5ae8 core/templates/partials/ai-list.html:29 | core/templates/partials/identifier-list.html:29 | core/templates/partials/group-list.html:39 | core/templates/partials/manipulation-list.html:67 | core/templates/partials/message-list.html:59 | core/templates/partials/person-list.html:41 | core/templates/partials/persona-list.html:47 | core/templates/partials/session-list.html:39
17 8 1 9abf8a2f282656184f30aa39a5691f1ba3c5f751 core/views/workspace.py:4435 | core/views/workspace.py:4532 | core/views/workspace.py:4533 | core/views/workspace.py:4784 | core/views/workspace.py:4785 | core/views/workspace.py:5049 | core/views/workspace.py:5119 | core/views/workspace.py:5143
18 6 6 fd8babf68e334229e8760b124291f08ba139e8da core/templates/partials/group-list.html:18 | core/templates/partials/manipulation-list.html:22 | core/templates/partials/message-list.html:24 | core/templates/partials/person-list.html:19 | core/templates/partials/persona-list.html:22 | core/templates/partials/session-list.html:18
19 6 6 f6142cb779dc039ab3c55f0cd88686d17b0bf715 core/templates/partials/group-list.html:22 | core/templates/partials/manipulation-list.html:26 | core/templates/partials/message-list.html:28 | core/templates/partials/person-list.html:23 | core/templates/partials/persona-list.html:26 | core/templates/partials/session-list.html:22
20 6 6 efeb0fa9dbc193eff23b464d67ca1fbf81d5bdbf core/templates/partials/group-list.html:25 | core/templates/partials/manipulation-list.html:29 | core/templates/partials/message-list.html:31 | core/templates/partials/person-list.html:26 | core/templates/partials/persona-list.html:29 | core/templates/partials/session-list.html:25
21 6 6 ea9c94038a099db8763e33d44239d2fc1ec791b5 core/templates/partials/group-list.html:19 | core/templates/partials/manipulation-list.html:23 | core/templates/partials/message-list.html:25 | core/templates/partials/person-list.html:20 | core/templates/partials/persona-list.html:23 | core/templates/partials/session-list.html:19
22 6 6 d34851ba01afdef301f4d95b748ac93567759315 core/templates/partials/group-list.html:21 | core/templates/partials/manipulation-list.html:25 | core/templates/partials/message-list.html:27 | core/templates/partials/person-list.html:22 | core/templates/partials/persona-list.html:25 | core/templates/partials/session-list.html:21
23 6 6 c198cfd05631c42f80c1cac33060bcae2a8314e1 core/templates/partials/group-list.html:24 | core/templates/partials/manipulation-list.html:28 | core/templates/partials/message-list.html:30 | core/templates/partials/person-list.html:25 | core/templates/partials/persona-list.html:28 | core/templates/partials/session-list.html:24
24 6 6 b7ffcc018c542592630ae4aa179df63fd73124c6 core/templates/partials/ai-list.html:35 | core/templates/partials/identifier-list.html:35 | core/templates/partials/group-list.html:45 | core/templates/partials/manipulation-list.html:73 | core/templates/partials/message-list.html:65 | core/templates/partials/persona-list.html:53
25 6 6 b0e7bdb0253104d309381cd1b92b8b1361011497 core/templates/partials/group-list.html:20 | core/templates/partials/manipulation-list.html:24 | core/templates/partials/message-list.html:26 | core/templates/partials/person-list.html:21 | core/templates/partials/persona-list.html:24 | core/templates/partials/session-list.html:20
26 6 6 642e4fd07d36ac1672d41a357245fe65d548a167 core/templates/partials/ai-list.html:34 | core/templates/partials/identifier-list.html:34 | core/templates/partials/group-list.html:44 | core/templates/partials/manipulation-list.html:72 | core/templates/partials/message-list.html:64 | core/templates/partials/persona-list.html:52
27 6 6 5a0aa6e4c55089cc2bc930f5b41e6460c03377be core/templates/partials/group-list.html:23 | core/templates/partials/manipulation-list.html:27 | core/templates/partials/message-list.html:29 | core/templates/partials/person-list.html:24 | core/templates/partials/persona-list.html:27 | core/templates/partials/session-list.html:23
28 6 3 d59a18a73345a4d9ee14cf53c0651e265283500f core/templates/two_factor/core/login.html:25 | core/templates/two_factor/core/login.html:26 | core/templates/two_factor/core/phone_register.html:15 | core/templates/two_factor/core/phone_register.html:16 | core/templates/two_factor/core/setup.html:47 | core/templates/two_factor/core/setup.html:48
29 6 1 f508ae701315bdaff81c2ed4fc909dcfe3063e12 core/views/workspace.py:4434 | core/views/workspace.py:4531 | core/views/workspace.py:4783 | core/views/workspace.py:5048 | core/views/workspace.py:5118 | core/views/workspace.py:5142
30 5 1 9e2147b111b0b4cfde1741ff031ed5eeee0fb90c core/views/workspace.py:3565 | core/views/workspace.py:3612 | core/views/workspace.py:3673 | core/views/workspace.py:3692 | core/views/workspace.py:3734
31 4 4 f1aa79edd9798ce0335811883d9edf5641caa560 core/views/groups.py:6 | core/views/manipulations.py:6 | core/views/people.py:6 | core/views/personas.py:6
32 4 4 f0ec5c6490de79592d8bc01b014f7dcccd0ef038 core/templates/partials/ai-list.html:44 | core/templates/partials/identifier-list.html:44 | core/templates/partials/message-list.html:74 | core/templates/partials/session-list.html:63
33 4 4 e8c131c539e745e9225a33c05a8eaa7373258d54 core/templates/pages/ai-workspace-insight-detail.html:4 | core/templates/pages/ai-workspace-insight-graphs.html:4 | core/templates/pages/ai-workspace-insight-help.html:3 | core/templates/pages/ai-workspace-information.html:3
34 4 4 decebca418375e5a29ab01c3754b762d2657233f core/templates/pages/ai-workspace-insight-detail.html:3 | core/templates/pages/ai-workspace-insight-graphs.html:3 | core/templates/pages/ai-workspace-insight-help.html:2 | core/templates/pages/ai-workspace-information.html:2
35 4 4 b7577e6b11ec4d0fa29bb51c1a83825ce02fd524 core/templates/partials/ai-list.html:42 | core/templates/partials/identifier-list.html:42 | core/templates/partials/message-list.html:72 | core/templates/partials/session-list.html:61
36 4 4 75767eb7e8ab76bbe8ef7adbdbcb3885d5f49928 core/templates/partials/ai-list.html:45 | core/templates/partials/identifier-list.html:45 | core/templates/partials/message-list.html:75 | core/templates/partials/session-list.html:64
37 4 4 1eb40cc61e3b91955ce88faa8fd0046ec6551929 core/templates/partials/ai-list.html:43 | core/templates/partials/identifier-list.html:43 | core/templates/partials/message-list.html:73 | core/templates/partials/session-list.html:62
38 4 2 cee0c018559f005cebe0b2d6b3b50dbaebc40d66 core/templates/pages/compose-contact-match.html:22 | core/templates/pages/compose-contact-match.html:23 | core/templates/pages/system-settings.html:8 | core/templates/pages/system-settings.html:9
39 4 2 c9cfa57fda2e778ee9b5eada947cbf891b6664e9 core/views/signal.py:53 | core/views/whatsapp.py:52 | core/views/whatsapp.py:93 | core/views/whatsapp.py:127
40 4 2 445d072fad4a4544aa92401013bc2a116fa85412 core/views/signal.py:54 | core/views/whatsapp.py:53 | core/views/whatsapp.py:94 | core/views/whatsapp.py:128
41 4 2 07396bab972b3c399891794476328d05274a7b24 core/views/signal.py:55 | core/views/whatsapp.py:54 | core/views/whatsapp.py:95 | core/views/whatsapp.py:129
42 4 1 fc8954f193dbb953ac30413e97ca67dcc1995196 core/views/whatsapp.py:214 | core/views/whatsapp.py:245 | core/views/whatsapp.py:311 | core/views/whatsapp.py:346
43 4 1 f6b88ff8738ebf9460faad2c3e174be762c67293 core/views/osint.py:147 | core/views/osint.py:205 | core/views/osint.py:244 | core/views/osint.py:322
44 4 1 e439fdd1e1150add02fabd3c9eba6b2042956e5b core/views/workspace.py:4581 | core/views/workspace.py:4635 | core/views/workspace.py:4710 | core/views/workspace.py:4749
45 4 1 dbcf5eed0e71ad85b4292e3046783ef1c09313e7 core/views/workspace.py:4582 | core/views/workspace.py:4636 | core/views/workspace.py:4711 | core/views/workspace.py:4750
46 4 1 c6fd3be26b4dac1b081caf2c23fe4017cb32c79d core/views/osint.py:151 | core/views/osint.py:209 | core/views/osint.py:248 | core/views/osint.py:326
47 4 1 619a8132534bf4cf49cb9025caefc4368d23027a core/views/workspace.py:3983 | core/views/workspace.py:4001 | core/views/workspace.py:4112 | core/views/workspace.py:4149
48 4 1 55794b46a1c1398e2854efcd5956b72457efafbc core/views/osint.py:150 | core/views/osint.py:208 | core/views/osint.py:247 | core/views/osint.py:325
49 4 1 51ed1c5494a45d890fab39751a78358c70a665d2 core/views/workspace.py:4583 | core/views/workspace.py:4637 | core/views/workspace.py:4712 | core/views/workspace.py:4751
50 4 1 362730b92c130e505e810721f050388d6dca84ea core/views/workspace.py:3566 | core/views/workspace.py:3674 | core/views/workspace.py:3693 | core/views/workspace.py:3735
51 4 1 28331d9d604508bf90644afe2bca3459260288d3 core/views/osint.py:148 | core/views/osint.py:206 | core/views/osint.py:245 | core/views/osint.py:323
52 4 1 24492ca0de0ef20daca922e99241c19e2b4476d9 core/views/compose.py:2829 | core/views/compose.py:2890 | core/views/compose.py:2953 | core/views/compose.py:3069
53 4 1 0429ccd645a7c781c66863de386fff245096acb8 core/templates/partials/signal-chats-list.html:55 | core/templates/partials/signal-chats-list.html:64 | core/templates/partials/signal-chats-list.html:100 | core/templates/partials/signal-chats-list.html:108
54 4 1 00402420dfa95d67fb1fa047200c2c7ebec9e09f core/views/osint.py:149 | core/views/osint.py:207 | core/views/osint.py:246 | core/views/osint.py:324
55 3 3 f72bfbb1fa3aaba54774035a28fa32d40d07782e core/templates/partials/whatsapp-contacts-list.html:188 | core/templates/pages/compose-contact-match.html:322 | core/templates/partials/osint/list-table.html:320
56 3 3 ef0d9a8dd3eba020022cc111353fac4dede707aa core/templates/partials/whatsapp-contacts-list.html:189 | core/templates/pages/compose-contact-match.html:323 | core/templates/partials/osint/list-table.html:321
57 3 3 e93f48a528a0b829c15e836f52b0688302294cd7 core/templates/mixins/window-content/persona-form.html:3 | core/templates/mixins/window-content/queue-form-inline.html:3 | core/templates/mixins/window-content/person-form.html:3
58 3 3 e78a723340a9cdd4c8342598dd77df9905695934 core/templates/partials/group-list.html:54 | core/templates/partials/manipulation-list.html:82 | core/templates/partials/person-list.html:65
59 3 3 d2066ceb8eb4188b1dda46b55168a971bab01fa8 core/templates/partials/group-list.html:55 | core/templates/partials/manipulation-list.html:83 | core/templates/partials/person-list.html:66
60 3 3 c28449d0cb2fbbd1ae9a2fcf1894e23fe691b28f core/templates/mixins/window-content/persona-form.html:120 | core/templates/mixins/window-content/queue-form-inline.html:36 | core/templates/mixins/window-content/person-form.html:127
61 3 3 b95648f6e16638f41aa87271ea707743183c6c07 core/templates/mixins/window-content/persona-form.html:35 | core/templates/mixins/window-content/queue-form-inline.html:20 | core/templates/mixins/window-content/person-form.html:41
62 3 3 9bf9b960c86ce18ec24c06722eae061ba6c53b0d core/templates/mixins/window-content/persona-form.html:33 | core/templates/mixins/window-content/queue-form-inline.html:18 | core/templates/mixins/window-content/person-form.html:39
63 3 3 90ecfd0cc6216452e3e1cf70802b6c5bf31bb6e1 core/templates/registration/login.html:1 | core/templates/registration/registration_closed.html:1 | core/templates/registration/signup.html:1
64 3 3 908efc2243afc21fdb03a03590b487053bd8df62 core/views/signal.py:27 | core/views/system.py:136 | core/views/whatsapp.py:23
65 3 3 8d46d1c156fa9b3f9ada7cfa9a2307e47cbd1d7d core/templates/mixins/window-content/persona-form.html:4 | core/templates/mixins/window-content/queue-form-inline.html:4 | core/templates/mixins/window-content/person-form.html:4
66 3 3 88de0e7ee40636f90fdc006951eecceab664b9d9 core/templates/registration/login.html:2 | core/templates/registration/registration_closed.html:2 | core/templates/registration/signup.html:2
67 3 3 8298593e6112ae5b1b79d784687dbe0adf81986c core/templates/mixins/window-content/persona-form.html:31 | core/templates/mixins/window-content/queue-form-inline.html:16 | core/templates/mixins/window-content/person-form.html:37
68 3 3 7faa673ea237e033b1c17cfedd0ea28aa663a47f core/templates/two_factor/core/login.html:24 | core/templates/two_factor/core/phone_register.html:14 | core/templates/two_factor/core/setup.html:46
69 3 3 70dd01d1657eb83f2e2683cb57ddb48c6c096461 core/templates/mixins/window-content/persona-form.html:2 | core/templates/mixins/window-content/queue-form-inline.html:2 | core/templates/mixins/window-content/person-form.html:2
70 3 3 57a7ed538e23b8b8fe2fe43f7de9a034ca5dc09e core/templates/registration/login.html:3 | core/templates/registration/registration_closed.html:3 | core/templates/registration/signup.html:3
71 3 3 46899ae970e49b3abf0ee240164475e500a508af core/templates/mixins/window-content/persona-form.html:32 | core/templates/mixins/window-content/queue-form-inline.html:17 | core/templates/mixins/window-content/person-form.html:38
72 3 3 39756b7a97a94b78848422a3db963aca123f793f core/templates/partials/whatsapp-contacts-list.html:163 | core/templates/pages/compose-contact-match.html:293 | core/templates/partials/osint/list-table.html:301
73 3 3 1c17b1165c7bdf1f43b33059f711f6f85b9cc218 core/templates/registration/login.html:4 | core/templates/registration/registration_closed.html:4 | core/templates/registration/signup.html:4
74 3 3 1be799390181879613097b249220ae621ff07cdb core/templates/partials/whatsapp-contacts-list.html:127 | core/templates/pages/compose-contact-match.html:447 | core/templates/partials/osint/list-table.html:261
75 3 3 19aab351002986a405384ecd3ce7608eb76d8bc5 core/templates/partials/group-list.html:53 | core/templates/partials/manipulation-list.html:81 | core/templates/partials/person-list.html:64
76 3 3 14aff65587a9bbd6eb7756ac5c8efd169b560a3a core/templates/partials/whatsapp-contacts-list.html:126 | core/templates/pages/compose-contact-match.html:446 | core/templates/partials/osint/list-table.html:260
77 3 3 14919fe1ff418d66f3d00bc460657f29d2fd8526 core/templates/partials/whatsapp-contacts-list.html:187 | core/templates/pages/compose-contact-match.html:321 | core/templates/partials/osint/list-table.html:319
78 3 3 0aca7b3de6e51826f65cb5b05b2384c26dfbbe46 core/templates/mixins/window-content/persona-form.html:34 | core/templates/mixins/window-content/queue-form-inline.html:19 | core/templates/mixins/window-content/person-form.html:40
79 3 3 0641ecc380ba0553c7878bf54bf39cb608f34fec core/templates/partials/group-list.html:52 | core/templates/partials/manipulation-list.html:80 | core/templates/partials/person-list.html:63
80 3 1 ff9e899aa632040f0c8c04c35c724a584e96170e core/views/workspace.py:4613 | core/views/workspace.py:4727 | core/views/workspace.py:4769
81 3 1 fd59c665fdc5e87a6d79967a146b4bdce2522e26 core/views/workspace.py:2277 | core/views/workspace.py:2278 | core/views/workspace.py:2279
82 3 1 e9480c8124135a1145d5b7f1adbad08d521efef4 core/views/whatsapp.py:66 | core/views/whatsapp.py:110 | core/views/whatsapp.py:142
83 3 1 e5fea1892e0910137b87aec081e0f617647d9e3c core/views/workspace.py:977 | core/views/workspace.py:978 | core/views/workspace.py:979
84 3 1 e28608b35e727724aecee511c796d18764cf672e core/templates/partials/compose-panel.html:126 | core/templates/partials/compose-panel.html:135 | core/templates/partials/compose-panel.html:144
85 3 1 dcd7eefc5742cbb8365f3f1f75183a7cc7615749 core/views/osint.py:155 | core/views/osint.py:213 | core/views/osint.py:330
86 3 1 d9b54261b181ce1f8c5072e7c8a1df8b3d001a77 core/views/workspace.py:3498 | core/views/workspace.py:3499 | core/views/workspace.py:3500
87 3 1 d975831bc528875ccfb5b7de5482552df7e943d5 core/views/compose.py:2861 | core/views/compose.py:2916 | core/views/compose.py:3077
88 3 1 d931b65a39674b3ee1691f50052d5e14b7df0c75 core/views/osint.py:146 | core/views/osint.py:204 | core/views/osint.py:321
89 3 1 d7908443bf8c1ca621dd346a0f86ef0bff940ab3 core/views/osint.py:156 | core/views/osint.py:214 | core/views/osint.py:331
90 3 1 d6b5f871e150b61a5e188e49f6dff02004193b3f core/views/compose.py:1001 | core/views/compose.py:1025 | core/views/compose.py:1351
91 3 1 d5c2ada1785825894266d98c57132b2544285032 core/views/compose.py:2830 | core/views/compose.py:2891 | core/views/compose.py:3070
92 3 1 d0b3c17ee7292bc9b72a6b225e7a1e60e9b205df core/views/compose.py:1002 | core/views/compose.py:1026 | core/views/compose.py:1352
93 3 1 cf1c4409875350836b698858dd25c6ecbde16919 core/views/compose.py:1000 | core/views/compose.py:1024 | core/views/compose.py:1350
94 3 1 c3d51fd612a0951656a5ecfa90f43e6d01f74397 core/views/signal.py:14 | core/views/signal.py:15 | core/views/signal.py:16
95 3 1 c07232d37f44c24fd72a425d5f3118b44ea878f4 core/views/workspace.py:3572 | core/views/workspace.py:3618 | core/views/workspace.py:4012
96 3 1 9c0c4d771d15fd5b044aaa34f60455b6a0162e41 core/views/workspace.py:4903 | core/views/workspace.py:4928 | core/views/workspace.py:4992
97 3 1 9b90da14fc1b8dbb3a77a0fe5e33ff8100ce8521 core/views/workspace.py:829 | core/views/workspace.py:830 | core/views/workspace.py:831
98 3 1 93f4f5c3e371c438ae0c43856c3828f6d117f72a core/views/compose.py:2831 | core/views/compose.py:2892 | core/views/compose.py:3071
99 3 1 90816cacf56a5067ae41905f8b55fe9d29537860 core/views/workspace.py:4612 | core/views/workspace.py:4726 | core/views/workspace.py:4768
100 3 1 8684b5111e67349fe997efcd70aafcea4fdf83e5 core/views/whatsapp.py:65 | core/views/whatsapp.py:109 | core/views/whatsapp.py:141
101 3 1 84ec68e536092f636e85f5fa8b8c62e132f8cf44 core/views/whatsapp.py:64 | core/views/whatsapp.py:108 | core/views/whatsapp.py:140
102 3 1 833c0a4b126b5483e2d9a371decbb6e64d8be31b core/views/osint.py:154 | core/views/osint.py:212 | core/views/osint.py:329
103 3 1 7fd26a8f251b682cc6532ea3e4b940e3c9d724f8 core/views/osint.py:158 | core/views/osint.py:216 | core/views/osint.py:333
104 3 1 6761ee7ea5444b7947203328998c87732446c26b core/views/osint.py:152 | core/views/osint.py:210 | core/views/osint.py:327
105 3 1 5d3621d8d1df3d692b03d7496a88f1983344f910 core/views/compose.py:997 | core/views/compose.py:1021 | core/views/compose.py:1347
106 3 1 5bf9dc3e0e46be35ae97b3d487cd626913f4917e core/views/instagram.py:3 | core/views/instagram.py:4 | core/views/instagram.py:5
107 3 1 59eb8ce9add8ac3ca30c2bd7910d926f4c1acf71 core/views/osint.py:153 | core/views/osint.py:211 | core/views/osint.py:328
108 3 1 595d7274dc3c2036cb637962dbdb26e5b72cd48d core/views/compose.py:2371 | core/views/compose.py:2580 | core/views/compose.py:2641
109 3 1 58d137eecf70203c332d9ac8eedf22185a5de4b3 core/views/workspace.py:3672 | core/views/workspace.py:3691 | core/views/workspace.py:3733
110 3 1 49cbc2c109a4ef6091f5fef70963ffbde0fa802d core/views/workspace.py:4164 | core/views/workspace.py:4243 | core/views/workspace.py:4300
111 3 1 481567b5ee2d425e4c04ce614ec8190ac6736a58 core/templates/partials/compose-panel.html:2667 | core/templates/partials/compose-panel.html:2729 | core/templates/partials/compose-panel.html:2751
112 3 1 42c0f869021d8ad0c15da6c4463dba6d2ec11643 core/views/compose.py:1003 | core/views/compose.py:1027 | core/views/compose.py:1353
113 3 1 421098a2efd474573beaccceeb159a576ee45114 core/views/workspace.py:4163 | core/views/workspace.py:4242 | core/views/workspace.py:4299
114 3 1 3ce072494ff8ce4a9e08df7924cb4038c02a0bc9 core/views/workspace.py:4614 | core/views/workspace.py:4728 | core/views/workspace.py:4770
115 3 1 3907486b4a80932f68c6b9354b3d9b0325c21d39 core/templates/partials/compose-panel.html:125 | core/templates/partials/compose-panel.html:134 | core/templates/partials/compose-panel.html:143
116 3 1 2c60e1087d82418be7362e9258da08450bc81c86 core/views/compose.py:2862 | core/views/compose.py:2917 | core/views/compose.py:3078
117 3 1 28720796d0312972c14eb707f03025ac3f574d16 core/views/osint.py:157 | core/views/osint.py:215 | core/views/osint.py:332
118 3 1 2492a12ee36707345d81b61907f4190db03c91cf core/views/workspace.py:1625 | core/views/workspace.py:1626 | core/views/workspace.py:1627
119 3 1 136f738cdbb963f2f131083bc6a4914baa489a99 core/templates/partials/ai-workspace-person-widget.html:581 | core/templates/partials/ai-workspace-person-widget.html:610 | core/templates/partials/ai-workspace-person-widget.html:647
120 3 1 112897fea978b734511faa475861bc87fb740ce4 core/views/compose.py:998 | core/views/compose.py:1022 | core/views/compose.py:1348
121 3 1 1112dde96781a149bcb3aa9da756f7f8a1d8cfc9 core/views/workspace.py:4520 | core/views/workspace.py:4559 | core/views/workspace.py:5033

View File

@@ -1465,13 +1465,10 @@ class WhatsAppClient(ClientBase):
# NOTE: Neonize get_all_contacts has crashed some runtime builds with a Go panic. # NOTE: Neonize get_all_contacts has crashed some runtime builds with a Go panic.
# Read contact-like rows directly from the session sqlite DB instead. # Read contact-like rows directly from the session sqlite DB instead.
contacts, source, lid_map = await self._sync_contacts_from_sqlite() contacts, source, lid_map = await self._sync_contacts_from_sqlite()
if not contacts: groups, groups_source = await self._sync_groups_from_client()
self.log.debug("whatsapp contacts sync empty (%s)", source or "unknown") now_ts = int(time.time())
self._publish_state(
last_event="contacts_sync_empty", if contacts:
contacts_source=source or "unknown",
)
return
self.log.debug( self.log.debug(
"whatsapp contacts synced: count=%s source=%s", "whatsapp contacts synced: count=%s source=%s",
len(contacts), len(contacts),
@@ -1480,12 +1477,29 @@ class WhatsAppClient(ClientBase):
self._publish_state( self._publish_state(
contacts=contacts, contacts=contacts,
lid_map=lid_map, lid_map=lid_map,
contacts_synced_at=int(time.time()), contacts_synced_at=now_ts,
contacts_sync_count=len(contacts), contacts_sync_count=len(contacts),
last_event="contacts_synced", last_event="contacts_synced",
contacts_source=source or "unknown", contacts_source=source or "unknown",
last_error="", last_error="",
) )
else:
self.log.debug("whatsapp contacts sync empty (%s)", source or "unknown")
self._publish_state(
last_event="contacts_sync_empty",
contacts_source=source or "unknown",
)
if groups_source:
event_name = "groups_synced" if groups else "groups_sync_empty"
self._publish_state(
groups=groups,
groups_source=groups_source,
groups_sync_count=len(groups),
groups_synced_at=now_ts,
last_event=event_name,
last_error="" if groups else "",
)
async def _sync_contacts_from_sqlite(self): async def _sync_contacts_from_sqlite(self):
def _extract(): def _extract():
@@ -1700,6 +1714,51 @@ class WhatsAppClient(ClientBase):
return await asyncio.to_thread(_extract) return await asyncio.to_thread(_extract)
async def _sync_groups_from_client(self):
if self._client is None:
return [], "client_missing"
getter = getattr(self._client, "get_joined_groups", None)
if getter is None:
return [], "get_joined_groups_missing"
try:
group_rows = await self._maybe_await(getter())
except Exception as exc:
self._publish_state(
last_event="groups_sync_failed",
last_error=str(exc),
)
return [], "get_joined_groups_failed"
out = []
now_ts = int(time.time())
for group in group_rows or []:
jid_value = self._jid_to_identifier(
self._pluck(group, "JID") or self._pluck(group, "jid")
)
identifier = (
jid_value.split("@", 1)[0].strip() if jid_value else ""
)
if not identifier:
continue
name = (
str(self._pluck(group, "GroupName", "Name") or "").strip()
or str(self._pluck(group, "GroupTopic", "Topic") or "").strip()
or identifier
)
out.append(
{
"identifier": identifier,
"jid": jid_value or f"{identifier}@g.us",
"name": name,
"chat": name,
"type": "group",
"seen_at": now_ts,
}
)
if len(out) >= 500:
break
return out, "get_joined_groups"
async def _is_contact_sync_ready(self) -> bool: async def _is_contact_sync_ready(self) -> bool:
if self._client is None: if self._client is None:
return False return False

View File

@@ -0,0 +1,56 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0024_workspacemetricsnapshot"),
]
operations = [
migrations.CreateModel(
name="PlatformChatLink",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("service", models.CharField(choices=[("signal", "Signal"), ("whatsapp", "WhatsApp"), ("xmpp", "XMPP"), ("instagram", "Instagram")], max_length=255)),
("chat_identifier", models.CharField(max_length=255)),
("chat_jid", models.CharField(blank=True, max_length=255, null=True)),
("chat_name", models.CharField(blank=True, max_length=255, null=True)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"person",
models.ForeignKey(on_delete=models.deletion.CASCADE, to="core.person"),
),
(
"person_identifier",
models.ForeignKey(blank=True, null=True, on_delete=models.deletion.SET_NULL, to="core.personidentifier"),
),
(
"user",
models.ForeignKey(on_delete=models.deletion.CASCADE, to="core.user"),
),
],
),
migrations.AddConstraint(
model_name="platformchatlink",
constraint=models.UniqueConstraint(
fields=("user", "service", "chat_identifier"),
name="unique_platform_chat_link",
),
),
migrations.AddIndex(
model_name="platformchatlink",
index=models.Index(
fields=["user", "service", "chat_identifier"],
name="core_platfo_user_id_0436ca_idx",
),
),
]

View File

@@ -4,6 +4,7 @@ import uuid
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from core.clients import transport from core.clients import transport
@@ -170,6 +171,62 @@ class PersonIdentifier(models.Model):
) )
class PlatformChatLink(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
person_identifier = models.ForeignKey(
PersonIdentifier,
on_delete=models.SET_NULL,
null=True,
blank=True,
)
service = models.CharField(choices=SERVICE_CHOICES, max_length=255)
chat_identifier = models.CharField(max_length=255)
chat_jid = models.CharField(max_length=255, blank=True, null=True)
chat_name = models.CharField(max_length=255, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["user", "service", "chat_identifier"],
name="unique_platform_chat_link",
)
]
indexes = [
models.Index(fields=["user", "service", "chat_identifier"]),
]
def clean(self):
if self.person_id and self.user_id and self.person.user_id != self.user_id:
raise ValidationError("Person must belong to the same user.")
if self.person_identifier_id:
if self.person_identifier.user_id != self.user_id:
raise ValidationError(
"Person identifier must belong to the same user."
)
if self.person_identifier.person_id != self.person_id:
raise ValidationError(
"Person identifier must belong to the selected person."
)
if self.person_identifier.service != self.service:
raise ValidationError(
"Chat links cannot be linked across platforms."
)
def save(self, *args, **kwargs):
value = str(self.chat_identifier or "").strip()
if "@" in value:
value = value.split("@", 1)[0]
self.chat_identifier = value
self.full_clean()
return super().save(*args, **kwargs)
def __str__(self):
return f"{self.person.name} ({self.service}: {self.chat_identifier})"
class ChatSession(models.Model): class ChatSession(models.Model):
"""Represents an ongoing chat session for persisted message history.""" """Represents an ongoing chat session for persisted message history."""

View File

@@ -0,0 +1,84 @@
{% extends "index.html" %}
{% block content %}
<section class="section">
<div class="container" style="max-width: 44rem;">
<div class="level" style="margin-bottom: 0.75rem;">
<div class="level-left">
<div>
<h1 class="title is-4" style="margin-bottom: 0.2rem;">WhatsApp Chat Link</h1>
<p class="is-size-7 has-text-grey">Link a WhatsApp chat identifier to a person. This link is WhatsApp-only.</p>
</div>
</div>
<div class="level-right">
<a class="button is-light" href="{% url 'whatsapp' %}">
<span class="icon is-small"><i class="fa-solid fa-arrow-left"></i></span>
<span>Back To WhatsApp</span>
</a>
</div>
</div>
{% if notice_message %}
<article class="notification is-{{ notice_level|default:'info' }} is-light">
{{ notice_message }}
</article>
{% endif %}
<article class="box">
<form method="post">
{% csrf_token %}
<div class="field">
<label class="label is-small">Chat Identifier</label>
<div class="control">
<input class="input" type="text" name="identifier" value="{{ identifier }}" required>
</div>
</div>
<div class="field">
<label class="label is-small">Existing Person</label>
<div class="select is-fullwidth">
<select name="person_id">
<option value="">- Select person -</option>
{% for person in people %}
<option value="{{ person.id }}" {% if existing and existing.person_id == person.id %}selected{% endif %}>{{ person.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="field">
<label class="label is-small">Or Create Person</label>
<div class="control">
<input class="input" type="text" name="person_name" placeholder="New person name">
</div>
</div>
<div class="field">
<label class="label is-small">Chat JID (optional)</label>
<div class="control">
<input class="input" type="text" name="chat_jid" value="{% if existing %}{{ existing.chat_jid|default:'' }}{% endif %}">
</div>
</div>
<div class="field">
<label class="label is-small">Display Name (optional)</label>
<div class="control">
<input class="input" type="text" name="chat_name" value="{% if existing %}{{ existing.chat_name|default:'' }}{% endif %}">
</div>
</div>
<button class="button is-link" type="submit">
<span class="icon is-small"><i class="fa-solid fa-link"></i></span>
<span>Save WhatsApp Chat Link</span>
</button>
</form>
</article>
{% if existing %}
<article class="notification is-light" style="margin-top: 0.8rem;">
Current link: <strong>{{ existing.person.name }}</strong><code>{{ existing.chat_identifier }}</code>
</article>
{% endif %}
</div>
</section>
{% endblock %}

View File

@@ -2545,18 +2545,6 @@
return response.json(); return response.json();
}; };
const getJson = async function (url) {
const response = await fetch(url, {
method: "GET",
credentials: "same-origin",
headers: { Accept: "application/json" }
});
if (!response.ok) {
throw new Error("Request failed");
}
return response.json();
};
const titleCase = function (value) { const titleCase = function (value) {
const raw = String(value || "").trim().toLowerCase(); const raw = String(value || "").trim().toLowerCase();
if (!raw) { if (!raw) {
@@ -2639,17 +2627,6 @@
} }
}; };
const cardContentNode = function (card) {
return card ? card.querySelector(".compose-ai-content") : null;
};
const setCardMessage = function (card, message) {
const node = cardContentNode(card);
if (node) {
node.textContent = String(message || "");
}
};
const openEngage = function (sourceRef) { const openEngage = function (sourceRef) {
const engageCard = showCard("engage"); const engageCard = showCard("engage");
if (!engageCard) { if (!engageCard) {
@@ -2669,19 +2646,19 @@
} }
setCardLoading(card, true); setCardLoading(card, true);
try { try {
const payload = await getJson( const response = await fetch(thread.dataset.draftsUrl + "?" + queryParams().toString(), {
thread.dataset.draftsUrl + "?" + queryParams().toString() method: "GET",
); credentials: "same-origin",
headers: { Accept: "application/json" }
});
const payload = await response.json();
setCardLoading(card, false); setCardLoading(card, false);
if (!payload.ok) { if (!payload.ok) {
setCardMessage(card, payload.error || "Failed to load drafts."); card.querySelector(".compose-ai-content").textContent = payload.error || "Failed to load drafts.";
return; return;
} }
const drafts = Array.isArray(payload.drafts) ? payload.drafts : []; const drafts = Array.isArray(payload.drafts) ? payload.drafts : [];
const container = cardContentNode(card); const container = card.querySelector(".compose-ai-content");
if (!container) {
return;
}
container.innerHTML = ""; container.innerHTML = "";
const engageButton = document.createElement("button"); const engageButton = document.createElement("button");
engageButton.type = "button"; engageButton.type = "button";
@@ -2720,7 +2697,7 @@
}); });
} catch (err) { } catch (err) {
setCardLoading(card, false); setCardLoading(card, false);
setCardMessage(card, "Failed to load drafts."); card.querySelector(".compose-ai-content").textContent = "Failed to load drafts.";
} }
}; };
@@ -2731,18 +2708,21 @@
} }
setCardLoading(card, true); setCardLoading(card, true);
try { try {
const payload = await getJson( const response = await fetch(thread.dataset.summaryUrl + "?" + queryParams().toString(), {
thread.dataset.summaryUrl + "?" + queryParams().toString() method: "GET",
); credentials: "same-origin",
headers: { Accept: "application/json" }
});
const payload = await response.json();
setCardLoading(card, false); setCardLoading(card, false);
if (!payload.ok) { if (!payload.ok) {
setCardMessage(card, payload.error || "Failed to load summary."); card.querySelector(".compose-ai-content").textContent = payload.error || "Failed to load summary.";
return; return;
} }
setCardMessage(card, String(payload.summary || "")); card.querySelector(".compose-ai-content").textContent = String(payload.summary || "");
} catch (err) { } catch (err) {
setCardLoading(card, false); setCardLoading(card, false);
setCardMessage(card, "Failed to load summary."); card.querySelector(".compose-ai-content").textContent = "Failed to load summary.";
} }
}; };
@@ -2753,14 +2733,17 @@
} }
setCardLoading(card, true); setCardLoading(card, true);
try { try {
const payload = await getJson( const response = await fetch(
thread.dataset.quickInsightsUrl + "?" + queryParams().toString() thread.dataset.quickInsightsUrl + "?" + queryParams().toString(),
); {
setCardLoading(card, false); method: "GET",
const container = cardContentNode(card); credentials: "same-origin",
if (!container) { headers: { Accept: "application/json" }
return;
} }
);
const payload = await response.json();
setCardLoading(card, false);
const container = card.querySelector(".compose-ai-content");
if (!payload.ok) { if (!payload.ok) {
container.textContent = payload.error || "Failed to load quick insights."; container.textContent = payload.error || "Failed to load quick insights.";
return; return;
@@ -3013,7 +2996,8 @@
} }
} catch (err) { } catch (err) {
setCardLoading(card, false); setCardLoading(card, false);
setCardMessage(card, "Failed to load quick insights."); card.querySelector(".compose-ai-content").textContent =
"Failed to load quick insights.";
} }
}; };
@@ -3053,12 +3037,18 @@
if (showCustom && customValue) { if (showCustom && customValue) {
params.set("custom_text", customValue); params.set("custom_text", customValue);
} }
const payload = await getJson( const response = await fetch(
thread.dataset.engagePreviewUrl + "?" + params.toString() thread.dataset.engagePreviewUrl + "?" + params.toString(),
{
method: "GET",
credentials: "same-origin",
headers: { Accept: "application/json" }
}
); );
const payload = await response.json();
setCardLoading(card, false); setCardLoading(card, false);
if (!payload.ok) { if (!payload.ok) {
setCardMessage(card, payload.error || "Failed to load engage preview."); card.querySelector(".compose-ai-content").textContent = payload.error || "Failed to load engage preview.";
panelState.engageToken = ""; panelState.engageToken = "";
return; return;
} }
@@ -3093,11 +3083,11 @@
if (payload.artifact) { if (payload.artifact) {
text = text + "\n\nSource: " + String(payload.artifact); text = text + "\n\nSource: " + String(payload.artifact);
} }
setCardMessage(card, text); card.querySelector(".compose-ai-content").textContent = text;
sendBtn.disabled = !(confirm.checked && panelState.engageToken); sendBtn.disabled = !(confirm.checked && panelState.engageToken);
} catch (err) { } catch (err) {
setCardLoading(card, false); setCardLoading(card, false);
setCardMessage(card, "Failed to load engage preview."); card.querySelector(".compose-ai-content").textContent = "Failed to load engage preview.";
panelState.engageToken = ""; panelState.engageToken = "";
} finally { } finally {
if (refreshBtn) { if (refreshBtn) {

View File

@@ -7,7 +7,7 @@ from django.views import View
from mixins.views import ObjectList, ObjectRead from mixins.views import ObjectList, ObjectRead
from core.clients import transport from core.clients import transport
from core.models import ChatSession, Message, PersonIdentifier from core.models import PersonIdentifier
from core.util import logs from core.util import logs
from core.views.compose import _compose_urls, _service_icon_class from core.views.compose import _compose_urls, _service_icon_class
from core.views.manage.permissions import SuperUserRequiredMixin from core.views.manage.permissions import SuperUserRequiredMixin
@@ -263,84 +263,102 @@ class WhatsAppChatsList(WhatsAppContactsList):
rows = [] rows = []
seen = set() seen = set()
state = transport.get_runtime_state("whatsapp") state = transport.get_runtime_state("whatsapp")
runtime_contacts = state.get("contacts") or [] runtime_contacts = state.get("contacts") or []
runtime_name_map = {} runtime_groups = state.get("groups") or []
for item in runtime_contacts: combined_contacts = []
if not isinstance(item, dict): for item in runtime_contacts + runtime_groups:
continue if isinstance(item, dict):
identifier = str(item.get("identifier") or "").strip() combined_contacts.append(item)
contact_index = {}
for item in combined_contacts:
raw_identifier = str(
item.get("identifier") or item.get("jid") or item.get("chat") or ""
).strip()
jid = str(item.get("jid") or "").strip()
name = str(item.get("name") or item.get("chat") or "").strip()
base_id = raw_identifier.split("@", 1)[0].strip()
jid_base = jid.split("@", 1)[0].strip()
for key in {raw_identifier, base_id, jid, jid_base}:
if key:
contact_index[key] = {"name": name, "jid": jid}
history_anchors = state.get("history_anchors") or {}
for key, anchor in (history_anchors.items() if isinstance(history_anchors, dict) else []):
identifier = str(key or "").strip()
if not identifier: if not identifier:
continue continue
runtime_name_map[identifier] = str(item.get("name") or "").strip() identifier = identifier.split("@", 1)[0].strip() or identifier
if identifier in seen:
sessions = (
ChatSession.objects.filter(
user=self.request.user,
identifier__service="whatsapp",
)
.select_related("identifier", "identifier__person")
.order_by("-last_interaction", "-id")
)
for session in sessions:
identifier = str(session.identifier.identifier or "").strip()
if not identifier or identifier in seen:
continue continue
seen.add(identifier) seen.add(identifier)
latest = ( anchor_jid = str((anchor or {}).get("chat_jid") or "").strip()
Message.objects.filter(user=self.request.user, session=session) contact = contact_index.get(identifier) or contact_index.get(anchor_jid)
.order_by("-ts") jid = (contact or {}).get("jid") or anchor_jid or identifier
.first() linked = self._linked_identifier(identifier, jid)
urls = _compose_urls(
"whatsapp",
identifier,
linked.person_id if linked else None,
) )
urls = _compose_urls("whatsapp", identifier, session.identifier.person_id) name = (
preview = str((latest.text if latest else "") or "").strip() (contact or {}).get("name")
if len(preview) > 80: or (linked.person.name if linked else "")
preview = f"{preview[:77]}..." or jid
display_name = ( or identifier
preview
or runtime_name_map.get(identifier)
or session.identifier.person.name
or "WhatsApp Chat" or "WhatsApp Chat"
) )
rows.append( rows.append(
{ {
"identifier": identifier, "identifier": identifier,
"jid": identifier, "jid": jid,
"name": display_name, "name": name,
"service_icon_class": _service_icon_class("whatsapp"), "service_icon_class": _service_icon_class("whatsapp"),
"person_name": session.identifier.person.name, "person_name": linked.person.name if linked else "",
"compose_page_url": urls["page_url"], "compose_page_url": urls["page_url"],
"compose_widget_url": urls["widget_url"], "compose_widget_url": urls["widget_url"],
"match_url": ( "match_url": (
f"{reverse('compose_contact_match')}?" f"{reverse('compose_contact_match')}?"
f"{urlencode({'service': 'whatsapp', 'identifier': identifier})}" f"{urlencode({'service': 'whatsapp', 'identifier': identifier})}"
), ),
"last_ts": int(latest.ts or 0) if latest else 0, "last_ts": int((anchor or {}).get("ts") or (anchor or {}).get("updated_at") or 0),
} }
) )
# Fallback: show synced WhatsApp contacts as chat entries even when no
# local message history exists yet. if rows:
for item in runtime_contacts: rows.sort(key=lambda row: row.get("last_ts", 0), reverse=True)
if not isinstance(item, dict): return rows
continue
identifier = str(item.get("identifier") or item.get("jid") or "").strip() # Fallback: if no anchors yet, surface the runtime contacts (best effort live state)
for item in combined_contacts:
identifier = str(
item.get("identifier") or item.get("jid") or item.get("chat") or ""
).strip()
if not identifier: if not identifier:
continue continue
identifier = identifier.split("@", 1)[0].strip() identifier = identifier.split("@", 1)[0].strip()
if not identifier or identifier in seen: if not identifier or identifier in seen:
continue continue
seen.add(identifier) seen.add(identifier)
linked = self._linked_identifier(identifier, str(item.get("jid") or "")) jid = str(item.get("jid") or "").strip()
linked = self._linked_identifier(identifier, jid)
urls = _compose_urls( urls = _compose_urls(
"whatsapp", "whatsapp",
identifier, identifier,
linked.person_id if linked else None, linked.person_id if linked else None,
) )
name = (
str(item.get("name") or item.get("chat") or "").strip()
or (linked.person.name if linked else "")
or jid
or identifier
or "WhatsApp Chat"
)
rows.append( rows.append(
{ {
"identifier": identifier, "identifier": identifier,
"jid": str(item.get("jid") or identifier).strip(), "jid": jid or identifier,
"name": str(item.get("name") or "WhatsApp Chat").strip() "name": name,
or "WhatsApp Chat",
"service_icon_class": _service_icon_class("whatsapp"), "service_icon_class": _service_icon_class("whatsapp"),
"person_name": linked.person.name if linked else "", "person_name": linked.person.name if linked else "",
"compose_page_url": urls["page_url"], "compose_page_url": urls["page_url"],
@@ -352,10 +370,7 @@ class WhatsAppChatsList(WhatsAppContactsList):
"last_ts": 0, "last_ts": 0,
} }
) )
if rows:
rows.sort(key=lambda row: row.get("last_ts", 0), reverse=True)
return rows return rows
return super().get_queryset(*args, **kwargs)
class WhatsAppAccountAdd(SuperUserRequiredMixin, ObjectRead): class WhatsAppAccountAdd(SuperUserRequiredMixin, ObjectRead):

View File

@@ -3497,12 +3497,6 @@ def _workspace_nav_urls(person):
} }
def _person_plan_or_404(request, person_id, plan_id):
person = get_object_or_404(Person, pk=person_id, user=request.user)
plan = get_object_or_404(PatternMitigationPlan, id=plan_id, user=request.user)
return person, plan
class AIWorkspace(LoginRequiredMixin, View): class AIWorkspace(LoginRequiredMixin, View):
template_name = "pages/ai-workspace.html" template_name = "pages/ai-workspace.html"
@@ -4437,7 +4431,12 @@ class AIWorkspaceMitigationChat(LoginRequiredMixin, View):
if type not in self.allowed_types: if type not in self.allowed_types:
return HttpResponseBadRequest("Invalid type specified") return HttpResponseBadRequest("Invalid type specified")
person, plan = _person_plan_or_404(request, person_id, plan_id) person = get_object_or_404(Person, pk=person_id, user=request.user)
plan = get_object_or_404(
PatternMitigationPlan,
id=plan_id,
user=request.user,
)
text = (request.POST.get("message") or "").strip() text = (request.POST.get("message") or "").strip()
active_tab = _sanitize_active_tab( active_tab = _sanitize_active_tab(
request.POST.get("active_tab"), default="ask_ai" request.POST.get("active_tab"), default="ask_ai"
@@ -4519,11 +4518,14 @@ class AIWorkspaceMitigationChat(LoginRequiredMixin, View):
text=assistant_text, text=assistant_text,
) )
return _render_mitigation_panel( return render(
request, request,
person, "partials/ai-workspace-mitigation-panel.html",
plan, _mitigation_panel_context(
person=person,
plan=plan,
active_tab=active_tab, active_tab=active_tab,
),
) )
@@ -4534,7 +4536,12 @@ class AIWorkspaceExportArtifact(LoginRequiredMixin, View):
if type not in self.allowed_types: if type not in self.allowed_types:
return HttpResponseBadRequest("Invalid type specified") return HttpResponseBadRequest("Invalid type specified")
person, plan = _person_plan_or_404(request, person_id, plan_id) person = get_object_or_404(Person, pk=person_id, user=request.user)
plan = get_object_or_404(
PatternMitigationPlan,
id=plan_id,
user=request.user,
)
artifact_type = (request.POST.get("artifact_type") or "rulebook").strip() artifact_type = (request.POST.get("artifact_type") or "rulebook").strip()
if artifact_type not in {"rulebook", "rules", "games", "corrections"}: if artifact_type not in {"rulebook", "rules", "games", "corrections"}:
@@ -4581,7 +4588,8 @@ class AIWorkspaceCreateArtifact(LoginRequiredMixin, View):
if type not in self.allowed_types: if type not in self.allowed_types:
return HttpResponseBadRequest("Invalid type specified") return HttpResponseBadRequest("Invalid type specified")
person, plan = _person_plan_or_404(request, person_id, plan_id) person = get_object_or_404(Person, pk=person_id, user=request.user)
plan = get_object_or_404(PatternMitigationPlan, id=plan_id, user=request.user)
kind_key = (kind or "").strip().lower() kind_key = (kind or "").strip().lower()
if kind_key not in self.kind_map: if kind_key not in self.kind_map:
return HttpResponseBadRequest("Invalid artifact kind") return HttpResponseBadRequest("Invalid artifact kind")
@@ -4635,7 +4643,8 @@ class AIWorkspaceUpdateArtifact(LoginRequiredMixin, View):
if type not in self.allowed_types: if type not in self.allowed_types:
return HttpResponseBadRequest("Invalid type specified") return HttpResponseBadRequest("Invalid type specified")
person, plan = _person_plan_or_404(request, person_id, plan_id) person = get_object_or_404(Person, pk=person_id, user=request.user)
plan = get_object_or_404(PatternMitigationPlan, id=plan_id, user=request.user)
kind_key = (kind or "").strip().lower() kind_key = (kind or "").strip().lower()
if kind_key not in self.kind_map: if kind_key not in self.kind_map:
return HttpResponseBadRequest("Invalid artifact kind") return HttpResponseBadRequest("Invalid artifact kind")
@@ -4710,7 +4719,8 @@ class AIWorkspaceDeleteArtifact(LoginRequiredMixin, View):
if type not in self.allowed_types: if type not in self.allowed_types:
return HttpResponseBadRequest("Invalid type specified") return HttpResponseBadRequest("Invalid type specified")
person, plan = _person_plan_or_404(request, person_id, plan_id) person = get_object_or_404(Person, pk=person_id, user=request.user)
plan = get_object_or_404(PatternMitigationPlan, id=plan_id, user=request.user)
kind_key = (kind or "").strip().lower() kind_key = (kind or "").strip().lower()
if kind_key not in self.kind_map: if kind_key not in self.kind_map:
return HttpResponseBadRequest("Invalid artifact kind") return HttpResponseBadRequest("Invalid artifact kind")
@@ -4749,7 +4759,8 @@ class AIWorkspaceDeleteArtifactList(LoginRequiredMixin, View):
if type not in self.allowed_types: if type not in self.allowed_types:
return HttpResponseBadRequest("Invalid type specified") return HttpResponseBadRequest("Invalid type specified")
person, plan = _person_plan_or_404(request, person_id, plan_id) person = get_object_or_404(Person, pk=person_id, user=request.user)
plan = get_object_or_404(PatternMitigationPlan, id=plan_id, user=request.user)
kind_key = (kind or "").strip().lower() kind_key = (kind or "").strip().lower()
if kind_key not in self.kind_map: if kind_key not in self.kind_map:
return HttpResponseBadRequest("Invalid artifact kind") return HttpResponseBadRequest("Invalid artifact kind")
@@ -4786,7 +4797,8 @@ class AIWorkspaceEngageShare(LoginRequiredMixin, View):
if type not in self.allowed_types: if type not in self.allowed_types:
return HttpResponseBadRequest("Invalid type specified") return HttpResponseBadRequest("Invalid type specified")
person, plan = _person_plan_or_404(request, person_id, plan_id) person = get_object_or_404(Person, pk=person_id, user=request.user)
plan = get_object_or_404(PatternMitigationPlan, id=plan_id, user=request.user)
source_ref = (request.POST.get("source_ref") or "").strip() source_ref = (request.POST.get("source_ref") or "").strip()
share_target = (request.POST.get("share_target") or "self").strip() share_target = (request.POST.get("share_target") or "self").strip()
@@ -5051,7 +5063,8 @@ class AIWorkspaceAutoSettings(LoginRequiredMixin, View):
if type not in self.allowed_types: if type not in self.allowed_types:
return HttpResponseBadRequest("Invalid type specified") return HttpResponseBadRequest("Invalid type specified")
person, plan = _person_plan_or_404(request, person_id, plan_id) person = get_object_or_404(Person, pk=person_id, user=request.user)
plan = get_object_or_404(PatternMitigationPlan, id=plan_id, user=request.user)
auto_settings = _get_or_create_auto_settings(request.user, plan.conversation) auto_settings = _get_or_create_auto_settings(request.user, plan.conversation)
auto_settings.enabled = _is_truthy(request.POST.get("enabled")) auto_settings.enabled = _is_truthy(request.POST.get("enabled"))
@@ -5121,7 +5134,8 @@ class AIWorkspaceUpdateFundamentals(LoginRequiredMixin, View):
if type not in self.allowed_types: if type not in self.allowed_types:
return HttpResponseBadRequest("Invalid type specified") return HttpResponseBadRequest("Invalid type specified")
person, plan = _person_plan_or_404(request, person_id, plan_id) person = get_object_or_404(Person, pk=person_id, user=request.user)
plan = get_object_or_404(PatternMitigationPlan, id=plan_id, user=request.user)
fundamentals_text = request.POST.get("fundamentals_text") or "" fundamentals_text = request.POST.get("fundamentals_text") or ""
active_tab = _sanitize_active_tab( active_tab = _sanitize_active_tab(
request.POST.get("active_tab"), default="fundamentals" request.POST.get("active_tab"), default="fundamentals"
@@ -5145,7 +5159,8 @@ class AIWorkspaceUpdatePlanMeta(LoginRequiredMixin, View):
if type not in self.allowed_types: if type not in self.allowed_types:
return HttpResponseBadRequest("Invalid type specified") return HttpResponseBadRequest("Invalid type specified")
person, plan = _person_plan_or_404(request, person_id, plan_id) person = get_object_or_404(Person, pk=person_id, user=request.user)
plan = get_object_or_404(PatternMitigationPlan, id=plan_id, user=request.user)
active_tab = _sanitize_active_tab( active_tab = _sanitize_active_tab(
request.POST.get("active_tab"), default="plan_board" request.POST.get("active_tab"), default="plan_board"
) )

View File

@@ -336,7 +336,7 @@ services:
source: /code/vrun source: /code/vrun
target: /var/run target: /var/run
healthcheck: healthcheck:
test: "redis-cli ping" test: "CMD-SHELL redis-cli -s /var/run/gia-redis.sock ping"
interval: 2s interval: 2s
timeout: 2s timeout: 2s
retries: 15 retries: 15

20
openspec/config.yaml Normal file
View File

@@ -0,0 +1,20 @@
schema: spec-driven
# Project context (optional)
# This is shown to AI when creating artifacts.
# Add your tech stack, conventions, style guides, domain knowledge, etc.
# Example:
# context: |
# Tech stack: TypeScript, React, Node.js
# We use conventional commits
# Domain: e-commerce platform
# Per-artifact rules (optional)
# Add custom rules for specific artifacts.
# Example:
# rules:
# proposal:
# - Keep proposals under 500 words
# - Always include a "Non-goals" section
# tasks:
# - Break tasks into chunks of max 2 hours