Continue AI features and improve protocol support
This commit is contained in:
@@ -10,12 +10,13 @@ from slixmpp.stanza import Message
|
||||
from slixmpp.xmlstream import register_stanza_plugin
|
||||
from slixmpp.xmlstream.stanzabase import ET
|
||||
|
||||
from core.clients import ClientBase, signalapi
|
||||
from core.clients import ClientBase
|
||||
from core.messaging import ai, history, replies, utils
|
||||
from core.models import (
|
||||
ChatSession,
|
||||
Manipulation,
|
||||
PatternMitigationAutoSettings,
|
||||
PatternMitigationCorrection,
|
||||
PatternMitigationGame,
|
||||
PatternMitigationPlan,
|
||||
PatternMitigationRule,
|
||||
@@ -91,21 +92,13 @@ class XMPPComponent(ComponentXMPP):
|
||||
sender_bare_jid = sender_parts[0] # Always present: user@domain
|
||||
sender_username, sender_domain = sender_bare_jid.split("@", 1)
|
||||
|
||||
sender_resource = (
|
||||
sender_parts[1] if len(sender_parts) > 1 else None
|
||||
) # Extract resource if present
|
||||
|
||||
# Extract recipient JID (should match component JID format)
|
||||
recipient_jid = str(msg["to"])
|
||||
|
||||
if "@" in recipient_jid:
|
||||
recipient_username, recipient_domain = recipient_jid.split("@", 1)
|
||||
recipient_username = recipient_jid.split("@", 1)[0]
|
||||
else:
|
||||
recipient_username = recipient_jid
|
||||
recipient_domain = recipient_jid
|
||||
|
||||
# Extract message body
|
||||
body = msg["body"] if msg["body"] else "[No Body]"
|
||||
# Parse recipient_name and recipient_service (e.g., "mark|signal")
|
||||
if "|" in recipient_username:
|
||||
person_name, service = recipient_username.split("|")
|
||||
@@ -134,9 +127,15 @@ class XMPPComponent(ComponentXMPP):
|
||||
return None
|
||||
|
||||
def _get_workspace_conversation(self, user, person):
|
||||
primary_identifier = (
|
||||
PersonIdentifier.objects.filter(user=user, person=person)
|
||||
.order_by("service")
|
||||
.first()
|
||||
)
|
||||
platform_type = primary_identifier.service if primary_identifier else "signal"
|
||||
conversation, _ = WorkspaceConversation.objects.get_or_create(
|
||||
user=user,
|
||||
platform_type="signal",
|
||||
platform_type=platform_type,
|
||||
title=f"{person.name} Workspace",
|
||||
defaults={"platform_thread_id": str(person.id)},
|
||||
)
|
||||
@@ -186,6 +185,10 @@ class XMPPComponent(ComponentXMPP):
|
||||
".mitigation rule-del <person>|<title> | "
|
||||
".mitigation game-add <person>|<title>|<instructions> | "
|
||||
".mitigation game-del <person>|<title> | "
|
||||
".mitigation correction-add <person>|<title>|<clarification> | "
|
||||
".mitigation correction-del <person>|<title> | "
|
||||
".mitigation fundamentals-set <person>|<item1;item2;...> | "
|
||||
".mitigation plan-set <person>|<draft|active|archived>|<auto|guided> | "
|
||||
".mitigation auto <person>|on|off | "
|
||||
".mitigation auto-status <person>"
|
||||
)
|
||||
@@ -214,7 +217,9 @@ class XMPPComponent(ComponentXMPP):
|
||||
if command.startswith(".mitigation show "):
|
||||
person_name = command.replace(".mitigation show ", "", 1).strip().title()
|
||||
person = await sync_to_async(
|
||||
lambda: Person.objects.filter(user=sender_user, name__iexact=person_name).first()
|
||||
lambda: Person.objects.filter(
|
||||
user=sender_user, name__iexact=person_name
|
||||
).first()
|
||||
)()
|
||||
if not person:
|
||||
sym("Unknown person.")
|
||||
@@ -231,9 +236,15 @@ class XMPPComponent(ComponentXMPP):
|
||||
if len(parts) < 3:
|
||||
sym("Usage: .mitigation rule-add <person>|<title>|<content>")
|
||||
return True
|
||||
person_name, title, content = parts[0].title(), parts[1], "|".join(parts[2:])
|
||||
person_name, title, content = (
|
||||
parts[0].title(),
|
||||
parts[1],
|
||||
"|".join(parts[2:]),
|
||||
)
|
||||
person = await sync_to_async(
|
||||
lambda: Person.objects.filter(user=sender_user, name__iexact=person_name).first()
|
||||
lambda: Person.objects.filter(
|
||||
user=sender_user, name__iexact=person_name
|
||||
).first()
|
||||
)()
|
||||
if not person:
|
||||
sym("Unknown person.")
|
||||
@@ -257,7 +268,9 @@ class XMPPComponent(ComponentXMPP):
|
||||
return True
|
||||
person_name, title = parts[0].title(), "|".join(parts[1:])
|
||||
person = await sync_to_async(
|
||||
lambda: Person.objects.filter(user=sender_user, name__iexact=person_name).first()
|
||||
lambda: Person.objects.filter(
|
||||
user=sender_user, name__iexact=person_name
|
||||
).first()
|
||||
)()
|
||||
if not person:
|
||||
sym("Unknown person.")
|
||||
@@ -279,9 +292,15 @@ class XMPPComponent(ComponentXMPP):
|
||||
if len(parts) < 3:
|
||||
sym("Usage: .mitigation game-add <person>|<title>|<instructions>")
|
||||
return True
|
||||
person_name, title, content = parts[0].title(), parts[1], "|".join(parts[2:])
|
||||
person_name, title, content = (
|
||||
parts[0].title(),
|
||||
parts[1],
|
||||
"|".join(parts[2:]),
|
||||
)
|
||||
person = await sync_to_async(
|
||||
lambda: Person.objects.filter(user=sender_user, name__iexact=person_name).first()
|
||||
lambda: Person.objects.filter(
|
||||
user=sender_user, name__iexact=person_name
|
||||
).first()
|
||||
)()
|
||||
if not person:
|
||||
sym("Unknown person.")
|
||||
@@ -305,7 +324,9 @@ class XMPPComponent(ComponentXMPP):
|
||||
return True
|
||||
person_name, title = parts[0].title(), "|".join(parts[1:])
|
||||
person = await sync_to_async(
|
||||
lambda: Person.objects.filter(user=sender_user, name__iexact=person_name).first()
|
||||
lambda: Person.objects.filter(
|
||||
user=sender_user, name__iexact=person_name
|
||||
).first()
|
||||
)()
|
||||
if not person:
|
||||
sym("Unknown person.")
|
||||
@@ -321,6 +342,128 @@ class XMPPComponent(ComponentXMPP):
|
||||
sym("Game deleted." if deleted else "Game not found.")
|
||||
return True
|
||||
|
||||
if command.startswith(".mitigation correction-add "):
|
||||
payload = command.replace(".mitigation correction-add ", "", 1)
|
||||
parts = parse_parts(payload)
|
||||
if len(parts) < 3:
|
||||
sym(
|
||||
"Usage: .mitigation correction-add <person>|<title>|<clarification>"
|
||||
)
|
||||
return True
|
||||
person_name, title, clarification = (
|
||||
parts[0].title(),
|
||||
parts[1],
|
||||
"|".join(parts[2:]),
|
||||
)
|
||||
person = await sync_to_async(
|
||||
lambda: Person.objects.filter(
|
||||
user=sender_user, name__iexact=person_name
|
||||
).first()
|
||||
)()
|
||||
if not person:
|
||||
sym("Unknown person.")
|
||||
return True
|
||||
plan = await sync_to_async(self._get_or_create_plan)(sender_user, person)
|
||||
await sync_to_async(PatternMitigationCorrection.objects.create)(
|
||||
user=sender_user,
|
||||
plan=plan,
|
||||
title=title[:255],
|
||||
clarification=clarification,
|
||||
source_phrase="",
|
||||
perspective="second_person",
|
||||
share_target="both",
|
||||
language_style="adapted",
|
||||
enabled=True,
|
||||
)
|
||||
sym("Correction added.")
|
||||
return True
|
||||
|
||||
if command.startswith(".mitigation correction-del "):
|
||||
payload = command.replace(".mitigation correction-del ", "", 1)
|
||||
parts = parse_parts(payload)
|
||||
if len(parts) < 2:
|
||||
sym("Usage: .mitigation correction-del <person>|<title>")
|
||||
return True
|
||||
person_name, title = parts[0].title(), "|".join(parts[1:])
|
||||
person = await sync_to_async(
|
||||
lambda: Person.objects.filter(
|
||||
user=sender_user, name__iexact=person_name
|
||||
).first()
|
||||
)()
|
||||
if not person:
|
||||
sym("Unknown person.")
|
||||
return True
|
||||
plan = await sync_to_async(self._get_or_create_plan)(sender_user, person)
|
||||
deleted, _ = await sync_to_async(
|
||||
lambda: PatternMitigationCorrection.objects.filter(
|
||||
user=sender_user,
|
||||
plan=plan,
|
||||
title__iexact=title,
|
||||
).delete()
|
||||
)()
|
||||
sym("Correction deleted." if deleted else "Correction not found.")
|
||||
return True
|
||||
|
||||
if command.startswith(".mitigation fundamentals-set "):
|
||||
payload = command.replace(".mitigation fundamentals-set ", "", 1)
|
||||
parts = parse_parts(payload)
|
||||
if len(parts) < 2:
|
||||
sym("Usage: .mitigation fundamentals-set <person>|<item1;item2;...>")
|
||||
return True
|
||||
person_name, values = parts[0].title(), "|".join(parts[1:])
|
||||
person = await sync_to_async(
|
||||
lambda: Person.objects.filter(
|
||||
user=sender_user, name__iexact=person_name
|
||||
).first()
|
||||
)()
|
||||
if not person:
|
||||
sym("Unknown person.")
|
||||
return True
|
||||
plan = await sync_to_async(self._get_or_create_plan)(sender_user, person)
|
||||
items = [item.strip() for item in values.split(";") if item.strip()]
|
||||
plan.fundamental_items = items
|
||||
await sync_to_async(plan.save)(
|
||||
update_fields=["fundamental_items", "updated_at"]
|
||||
)
|
||||
sym(f"Fundamentals updated ({len(items)}).")
|
||||
return True
|
||||
|
||||
if command.startswith(".mitigation plan-set "):
|
||||
payload = command.replace(".mitigation plan-set ", "", 1)
|
||||
parts = parse_parts(payload)
|
||||
if len(parts) < 3:
|
||||
sym(
|
||||
"Usage: .mitigation plan-set <person>|<draft|active|archived>|<auto|guided>"
|
||||
)
|
||||
return True
|
||||
person_name, status_value, mode_value = (
|
||||
parts[0].title(),
|
||||
parts[1].lower(),
|
||||
parts[2].lower(),
|
||||
)
|
||||
person = await sync_to_async(
|
||||
lambda: Person.objects.filter(
|
||||
user=sender_user, name__iexact=person_name
|
||||
).first()
|
||||
)()
|
||||
if not person:
|
||||
sym("Unknown person.")
|
||||
return True
|
||||
plan = await sync_to_async(self._get_or_create_plan)(sender_user, person)
|
||||
valid_status = {key for key, _ in PatternMitigationPlan.STATUS_CHOICES}
|
||||
valid_modes = {
|
||||
key for key, _ in PatternMitigationPlan.CREATION_MODE_CHOICES
|
||||
}
|
||||
if status_value in valid_status:
|
||||
plan.status = status_value
|
||||
if mode_value in valid_modes:
|
||||
plan.creation_mode = mode_value
|
||||
await sync_to_async(plan.save)(
|
||||
update_fields=["status", "creation_mode", "updated_at"]
|
||||
)
|
||||
sym(f"Plan updated: status={plan.status}, mode={plan.creation_mode}")
|
||||
return True
|
||||
|
||||
if command.startswith(".mitigation auto "):
|
||||
payload = command.replace(".mitigation auto ", "", 1)
|
||||
parts = parse_parts(payload)
|
||||
@@ -329,31 +472,47 @@ class XMPPComponent(ComponentXMPP):
|
||||
return True
|
||||
person_name, state = parts[0].title(), parts[1].lower()
|
||||
person = await sync_to_async(
|
||||
lambda: Person.objects.filter(user=sender_user, name__iexact=person_name).first()
|
||||
lambda: Person.objects.filter(
|
||||
user=sender_user, name__iexact=person_name
|
||||
).first()
|
||||
)()
|
||||
if not person:
|
||||
sym("Unknown person.")
|
||||
return True
|
||||
conversation = await sync_to_async(self._get_workspace_conversation)(sender_user, person)
|
||||
auto_obj, _ = await sync_to_async(PatternMitigationAutoSettings.objects.get_or_create)(
|
||||
conversation = await sync_to_async(self._get_workspace_conversation)(
|
||||
sender_user, person
|
||||
)
|
||||
auto_obj, _ = await sync_to_async(
|
||||
PatternMitigationAutoSettings.objects.get_or_create
|
||||
)(
|
||||
user=sender_user,
|
||||
conversation=conversation,
|
||||
)
|
||||
auto_obj.enabled = state in {"on", "true", "1", "yes"}
|
||||
await sync_to_async(auto_obj.save)(update_fields=["enabled", "updated_at"])
|
||||
sym(f"Automation {'enabled' if auto_obj.enabled else 'disabled'} for {person.name}.")
|
||||
sym(
|
||||
f"Automation {'enabled' if auto_obj.enabled else 'disabled'} for {person.name}."
|
||||
)
|
||||
return True
|
||||
|
||||
if command.startswith(".mitigation auto-status "):
|
||||
person_name = command.replace(".mitigation auto-status ", "", 1).strip().title()
|
||||
person_name = (
|
||||
command.replace(".mitigation auto-status ", "", 1).strip().title()
|
||||
)
|
||||
person = await sync_to_async(
|
||||
lambda: Person.objects.filter(user=sender_user, name__iexact=person_name).first()
|
||||
lambda: Person.objects.filter(
|
||||
user=sender_user, name__iexact=person_name
|
||||
).first()
|
||||
)()
|
||||
if not person:
|
||||
sym("Unknown person.")
|
||||
return True
|
||||
conversation = await sync_to_async(self._get_workspace_conversation)(sender_user, person)
|
||||
auto_obj, _ = await sync_to_async(PatternMitigationAutoSettings.objects.get_or_create)(
|
||||
conversation = await sync_to_async(self._get_workspace_conversation)(
|
||||
sender_user, person
|
||||
)
|
||||
auto_obj, _ = await sync_to_async(
|
||||
PatternMitigationAutoSettings.objects.get_or_create
|
||||
)(
|
||||
user=sender_user,
|
||||
conversation=conversation,
|
||||
)
|
||||
@@ -383,7 +542,7 @@ class XMPPComponent(ComponentXMPP):
|
||||
"""
|
||||
self.log.info(f"Chat state: Active from {msg['from']}.")
|
||||
|
||||
identifier = self.get_identifier(msg)
|
||||
self.get_identifier(msg)
|
||||
|
||||
def on_chatstate_composing(self, msg):
|
||||
"""
|
||||
@@ -392,6 +551,13 @@ class XMPPComponent(ComponentXMPP):
|
||||
self.log.info(f"Chat state: Composing from {msg['from']}.")
|
||||
|
||||
identifier = self.get_identifier(msg)
|
||||
if identifier:
|
||||
asyncio.create_task(
|
||||
self.ur.started_typing(
|
||||
"xmpp",
|
||||
identifier=identifier,
|
||||
)
|
||||
)
|
||||
|
||||
def on_chatstate_paused(self, msg):
|
||||
"""
|
||||
@@ -400,6 +566,13 @@ class XMPPComponent(ComponentXMPP):
|
||||
self.log.info(f"Chat state: Paused from {msg['from']}.")
|
||||
|
||||
identifier = self.get_identifier(msg)
|
||||
if identifier:
|
||||
asyncio.create_task(
|
||||
self.ur.stopped_typing(
|
||||
"xmpp",
|
||||
identifier=identifier,
|
||||
)
|
||||
)
|
||||
|
||||
def on_chatstate_inactive(self, msg):
|
||||
"""
|
||||
@@ -407,7 +580,7 @@ class XMPPComponent(ComponentXMPP):
|
||||
"""
|
||||
self.log.info(f"Chat state: Inactive from {msg['from']}.")
|
||||
|
||||
identifier = self.get_identifier(msg)
|
||||
self.get_identifier(msg)
|
||||
|
||||
def on_chatstate_gone(self, msg):
|
||||
"""
|
||||
@@ -415,7 +588,7 @@ class XMPPComponent(ComponentXMPP):
|
||||
"""
|
||||
self.log.info(f"Chat state: Gone from {msg['from']}.")
|
||||
|
||||
identifier = self.get_identifier(msg)
|
||||
self.get_identifier(msg)
|
||||
|
||||
def on_presence_available(self, pres):
|
||||
"""
|
||||
@@ -621,7 +794,9 @@ class XMPPComponent(ComponentXMPP):
|
||||
Process incoming XMPP messages.
|
||||
"""
|
||||
|
||||
sym = lambda x: msg.reply(f"[>] {x}").send()
|
||||
def sym(value):
|
||||
msg.reply(f"[>] {value}").send()
|
||||
|
||||
# self.log.info(f"Received message: {msg}")
|
||||
|
||||
# Extract sender JID (full format: user@domain/resource)
|
||||
@@ -710,7 +885,7 @@ class XMPPComponent(ComponentXMPP):
|
||||
|
||||
# Construct contact list response
|
||||
contact_names = [person.name for person in persons]
|
||||
response_text = f"Contacts: " + ", ".join(contact_names)
|
||||
response_text = "Contacts: " + ", ".join(contact_names)
|
||||
sym(response_text)
|
||||
elif body == ".help":
|
||||
sym("Commands: .contacts, .whoami, .mitigation help")
|
||||
@@ -785,12 +960,11 @@ class XMPPComponent(ComponentXMPP):
|
||||
)
|
||||
self.log.info(f"MANIP11 {manipulations}")
|
||||
if not manipulations:
|
||||
tss = await signalapi.send_message_raw(
|
||||
identifier.identifier,
|
||||
await identifier.send(
|
||||
body,
|
||||
attachments,
|
||||
)
|
||||
self.log.info(f"Message sent unaltered")
|
||||
self.log.info("Message sent unaltered")
|
||||
return
|
||||
|
||||
manip = manipulations.first()
|
||||
@@ -810,12 +984,11 @@ class XMPPComponent(ComponentXMPP):
|
||||
text=result,
|
||||
ts=int(now().timestamp() * 1000),
|
||||
)
|
||||
tss = await signalapi.send_message_raw(
|
||||
identifier.identifier,
|
||||
await identifier.send(
|
||||
result,
|
||||
attachments,
|
||||
)
|
||||
self.log.info(f"Message sent with modifications")
|
||||
self.log.info("Message sent with modifications")
|
||||
|
||||
async def request_upload_slots(self, recipient_jid, attachments):
|
||||
"""Requests upload slots for multiple attachments concurrently."""
|
||||
@@ -898,7 +1071,7 @@ class XMPPComponent(ComponentXMPP):
|
||||
|
||||
# Step 2: Request upload slots concurrently
|
||||
valid_uploads = await self.request_upload_slots(recipient_jid, attachments)
|
||||
self.log.info(f"Got upload slots")
|
||||
self.log.info("Got upload slots")
|
||||
if not valid_uploads:
|
||||
self.log.warning("No valid upload slots obtained.")
|
||||
# return
|
||||
|
||||
Reference in New Issue
Block a user