Skip to content

Business Logic

loglife.app.logic.text.processor

Message processing logic for inbound WhatsApp text commands.

process_text(user, message)

Route incoming text commands to the appropriate goal or rating handler.

Handle commands such as adding goals, submitting ratings, configuring reminder times, and generating look-back summaries. Maintain temporary state for multi-step flows (e.g., goal reminder setup).

Parameters:

Name Type Description Default
user User

The user record for the message sender

required
message str

The incoming text message content

required

Returns:

Type Description
str

The WhatsApp response text to send back to the user.

Source code in src/loglife/app/logic/text/processor.py
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def process_text(user: User, message: str) -> str:
    """Route incoming text commands to the appropriate goal or rating handler.

    Handle commands such as adding goals, submitting ratings, configuring
    reminder times, and generating look-back summaries. Maintain temporary
    state for multi-step flows (e.g., goal reminder setup).

    Arguments:
        user: The user record for the message sender
        message: The incoming text message content

    Returns:
        The WhatsApp response text to send back to the user.

    """
    try:
        message: str = message.strip().lower()

        # Add aliases (with word boundaries to avoid replacing partial words)
        for alias, command in COMMAND_ALIASES.items():
            # Escape alias to be safe in regex
            pattern = r"\b" + re.escape(alias) + r"\b"
            message = re.sub(pattern, command, message)

        # Execute the first matching command handler
        for handler in HANDLERS:
            if handler.matches(message):
                result = handler.handle(user, message)
                # Special case: add_goal can return None if no goal text provided
                if result is not None:
                    return result

    except Exception as exc:
        logger.exception("Error in text processor")
        return messages.ERROR_TEXT_PROCESSOR.format(exc=exc)

    return messages.ERROR_WRONG_COMMAND

loglife.app.logic.audio.processor

Audio processing workflow for inbound WhatsApp messages.

Orchestrates transcription (Whisper), summarization (GPT), and database storage of voice notes.

process_audio(sender, user, audio_data, client_type='whatsapp')

Process an incoming audio message from a user.

Parameters:

Name Type Description Default
sender str

The WhatsApp phone number of the sender

required
user User

The user record dictionary

required
audio_data str

Base64 encoded audio payload

required
client_type str

The client type to send responses to (default: "whatsapp")

'whatsapp'

Returns:

Type Description
str | tuple[str, str]

The summarized text generated from the audio, or a tuple of

str | tuple[str, str]

(transcript_file_base64, summarized_text).

Source code in src/loglife/app/logic/audio/processor.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def process_audio(
    sender: str,
    user: User,
    audio_data: str,
    client_type: str = "whatsapp",
) -> str | tuple[str, str]:
    """Process an incoming audio message from a user.

    Arguments:
        sender: The WhatsApp phone number of the sender
        user: The user record dictionary
        audio_data: Base64 encoded audio payload
        client_type: The client type to send responses to (default: "whatsapp")

    Returns:
        The summarized text generated from the audio, or a tuple of
        (transcript_file_base64, summarized_text).

    """
    queue_async_message(sender, "Audio received. Transcribing...", client_type=client_type)

    try:
        try:
            transcript: str = transcribe_audio(audio_data)
        except RuntimeError:
            logger.exception("Error transcribing audio")
            return "Transcription failed!"

        if not transcript.strip():
            return "Transcription was empty."

        queue_async_message(
            sender,
            "Audio transcribed. Summarizing...",
            client_type=client_type,
        )

        try:
            summary: str = summarize_transcript(transcript)
        except RuntimeError:
            logger.exception("Error summarizing transcript")
            return "Summarization failed!"

        db.audio_journals.create(
            user_id=user.id,
            transcription_text=transcript,
            summary_text=summary,
        )
        queue_async_message(
            sender,
            "Summary stored in Database.",
            client_type=client_type,
        )

        if user.send_transcript_file:
            transcript_file: str = transcript_to_base64(transcript)
            return transcript_file, summary

    except Exception as exc:
        logger.exception("Error in audio processor")
        return f"Error in audio processor: {exc}"

    return summary

loglife.app.logic.vcard.processor

Processing logic for referral VCARD payloads.

process_vcard(referrer_user, raw_vcards)

Create referral users from VCARD attachments.

Parse the incoming VCARD JSON payload, ensure each contact exists as a user, link referrals, and send a welcome message to each referred number.

Parameters:

Name Type Description Default
referrer_user User

The user dict of the person sharing the VCARDs

required
raw_vcards str

JSON string containing the VCARD data list

required

Returns:

Type Description
str

The referral success message constant.

Source code in src/loglife/app/logic/vcard/processor.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
def process_vcard(referrer_user: User, raw_vcards: str) -> str:
    """Create referral users from VCARD attachments.

    Parse the incoming VCARD JSON payload, ensure each contact exists as a
    user, link referrals, and send a welcome message to each referred number.

    Arguments:
        referrer_user: The user dict of the person sharing the VCARDs
        raw_vcards: JSON string containing the VCARD data list

    Returns:
        The referral success message constant.

    """
    try:
        vcards: list[str] = json.loads(raw_vcards)
        referrer_user_id: int = referrer_user.id

        for vcard in vcards:
            referred_phone_number = _extract_phone_number(vcard)
            if not referred_phone_number:
                continue

            referred_user: User | None = db.users.get_by_phone(referred_phone_number)
            if not referred_user:
                # Create new user with referrer
                db.users.create(
                    referred_phone_number,
                    "Asia/Karachi",
                    referred_by_id=referrer_user_id,
                )
                queue_async_message(referred_phone_number, WELCOME_MESSAGE, client_type="whatsapp")
            elif referred_user.referred_by_id is None:
                # Update existing user if they don't have a referrer
                db.users.update(referred_user.id, referred_by_id=referrer_user_id)

    except Exception as exc:
        logger.exception("Error in vcard processor")
        return f"Error in vcard processor: {exc}"

    return REFERRAL_SUCCESS