# Project export: 2nd Chance

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2024
- Tagline: Wish you spent more time talking to someone when you had the chance? 2nd Chance is your solution; by synthesizing your records of them, we simulate your loved one to grant you insight into their life.
- Devpost: https://devpost.com/software/2nd-chance-ryczl2
- GitHub: https://github.com/adeng27/treehacks
- Demo: http://2ndchance.vercel.app/
- Video: https://player.vimeo.com/video/914138632?byline=0&portrait=0&title=0#t=
- Team: 2 GitHub contributor(s) — Alastair Deng (26 commits), mrTSB (8 commits)

## Devpost submission (written by the team)

### Inspiration

We've all wished that we would've talked more to people in person when we had the chance - whether you need them to answer an urgent question, your schedules are too busy for frequent phone calls, or your loved one has passed on, 2nd Chance was built to let you talk to a loved one in real time. Created with the next generation of senior citizens in mind, we wanted to take advantage of the abundance of video and text records people will have of each other while implementing senior-friendly UI. We were excited to use new APIs and learn vector embedding techniques to accomplish our goal of simulating an individual based on their records.

### What it does

2nd Chance first requires a user to submit audio and/or text records of their loved one to get a sense of how they talk. Then, taking either a written or spoken input from the user, the software processes the input and compares it to the records on hand with vector embedding. If the user's input is spoken, it uses OpenAI's Whisper API to transcribe the message into written text. Using ChatGPT to synthesize the existing responses in the records, the software predicts a response to the user's entry. If the user's entry was spoken, we reproduce their loved one's voice using ElevenLab's API so they can speak their response back to the user.

### How we built it

The frontend and backend of our web application was built with Next.js and Convex, respectively. We specifically took advantage of Convex’s Vector Search, Server Functions, and Database features. Our AI simulation was powered by OpenAI’s Chat-GPT model and Vector Embeddings (for Vector Search) were provided by OpenAI’s embedding model. We use Gradio for our speech-to-speech interface, Whisper for audio transcription, and ElevenLabs for voice cloning. Finally, we used Tailwind CSS and Flowbite for overall styling.

### Challenges we ran into

Our decision to use Next.js and Convex caused some difficulty when we attempted to integrate our speech-to-speech software. We were required to implement that software in separate Python files, but then we lost access to our Vector Search software (through Convex). In the end, both speech-to-speech and text-to-text software were integrated onto the web application with Vector Search.

### Accomplishments we're proud of

We're proud of being able to produce an audio response in the voice of the user's loved one by using ElevenLab's API. It was exciting to get to implement a variety of APIs we hadn't used before to improve our functionality. Another accomplishment was getting the vector embedding to work to help generate responses specific to the user. We’re primarily proud of getting to help people from multiple backgrounds in their day to day lives.

### What we learned

We learned about implementing APIs using their documentations, as well as seeing how many APIs are out there and the value of open source code. We also learned that we should plan out all the technologies we want to use so that we can ensure they can be integrated properly. Lastly, we saw how much we could code we could learn and put together in the span of 36 hours.

### What's next

We want to continue to improve the integration of the AI speech-to-speech technology with the front-end design of our website to increase usability and design. We also want to ensure that our user’s text logs and/or audio recordings that they submit as reference points are well encrypted to protect their privacy. It would be nice to also increase functionality for non-English speakers to make our technology more accessible for diverse populations.

## README (from the GitHub repository)

This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.


## Detected evidence (automated analysis)

Indexed codebase: 24 recognized source files, 86 KB.
- CSS (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (32 of 32)

```
.eslintrc.json
.gitignore
convex/_generated/api.d.ts
convex/_generated/api.js
convex/_generated/dataModel.d.ts
convex/_generated/server.d.ts
convex/_generated/server.js
convex/chat.ts
convex/lib/openai.ts
convex/README.md
convex/realMessage.ts
convex/schema.ts
convex/tsconfig.json
embeddings.py
next.config.mjs
package.json
postcss.config.js
processData.py
public/+17737937551.txt
public/output.csv
README.md
speechToSpeech.py
src/app/addshit/page.tsx
src/app/components/navbar.tsx
src/app/ConvexClientProvider.tsx
src/app/globals.css
src/app/layout.tsx
src/app/page.tsx
src/app/test/page.tsx
src/app/test/test.css
tailwind.config.ts
tsconfig.json
```

### Dependencies

- package.json: @types/node@^20, @types/react@^18, @types/react-dom@^18, autoprefixer@^10.0.1, convex@^1.9.0, eslint@^8, eslint-config-next@14.1.0, next@14.1.0, openai@^4.28.0, postcss@^8, react@^18, react-dom@^18, tailwindcss@^3.3.0, typescript@^5

### Recent commits (newest first)

- DUMBLEDORE LIVES
- links live
- fix
- deploy for video
- fix
- system message
- gpt-4
- frontend
- python code speech to speech
- Make it look like chatGPT
- navbar plus form
- test page
- fix
- Merge branch 'main' of github.com:adeng27/treehacks
- joseph-fied voice + texts
- Ignoring the parsed file in gitignore
- lots of things
- Merge branch 'main' of github.com:adeng27/treehacks
- past messages in chat
- Merge branch 'main' of https://github.com/adeng27/treehacks

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "treehacks",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "convex": "^1.9.0",
    "next": "14.1.0",
    "openai": "^4.28.0",
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "autoprefixer": "^10.0.1",
    "eslint": "^8",
    "eslint-config-next": "14.1.0",
    "postcss": "^8",
    "tailwindcss": "^3.3.0",
    "typescript": "^5"
  }
}

```

### src/app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import ConvexClientProvider from "./ConvexClientProvider";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "2nd Chance",
  description: "Generated by create next app",
  icons: "/THLogo.png"
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <ConvexClientProvider>{children}</ConvexClientProvider>
      </body>
    </html>
  );
}

```

### convex/_generated/server.js

```javascript
/* eslint-disable */
/**
 * Generated utilities for implementing server-side Convex query and mutation functions.
 *
 * THIS CODE IS AUTOMATICALLY GENERATED.
 *
 * Generated by convex@1.9.0.
 * To regenerate, run `npx convex dev`.
 * @module
 */

import {
  actionGeneric,
  httpActionGeneric,
  queryGeneric,
  mutationGeneric,
  internalActionGeneric,
  internalMutationGeneric,
  internalQueryGeneric,
} from "convex/server";

/**
 * Define a query in this Convex app's public API.
 *
 * This function will be allowed to read your Convex database and will be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const query = queryGeneric;

/**
 * Define a query that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to read from your Convex database. It will not be accessible from the client.
 *
 * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
 * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
 */
export const internalQuery = internalQueryGeneric;

/**
 * Define a mutation in this Convex app's public API.
 *
 * This function will be allowed to modify your Convex database and will be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const mutation = mutationGeneric;

/**
 * Define a mutation that is only accessible from other Convex functions (but not from the client).
 *
 * This function will be allowed to modify your Convex database. It will not be accessible from the client.
 *
 * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
 * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
 */
export const internalMutation = internalMutationGeneric;

/**
 * Define an action in this Convex app's public API.
 *
 * An action is a function which can execute any JavaScript code, including non-deterministic
 * code and code with side-effects, like calling third-party services.
 * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
 * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
 *
 * @param func - The action. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
 */
export const action = actionGeneric;

/**
 * Define an action that is only accessible from other Convex functions (but not from the client).
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument.
 * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
 */
export const internalAction = internalActionGeneric;

/**
 * Define a Convex HTTP action.
 *
 * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
 * as its second.
 * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
 */
export const httpAction = httpActionGeneric;

```

### src/app/page.tsx

```typescript
"use client"

import { useAction, useQuery } from "convex/react";
import { useState } from "react";
import { api } from "../../convex/_generated/api";
import { Navbar } from "./components/navbar";

export default function Home() {
  const handleMessage = useAction(api.chat.handleMessage);
  // const makeTranscript = useAction(api.chat.makeTranscript);
  // const similarMessages = useAction(api.realMessage.similarInputs);
  // const insertMessages = useAction(api.realMessage.handleMessageSubmit);
  const entries = useQuery(api.chat.getAllEntries);
  const [message, setMessage] = useState("");

  return (
    // <main className="flex min-h-screen flex-col items-center justify-between p-24">
    <main className="h-screen w-screen">
      <Navbar />
      <div className="h-full w-full overflow-y-scroll pb-52 bg-white">
        {entries?.map(entry => {
          return (
            <div key={entry._id}>
              {/* <p>You: {entry.input}</p> */}
              <div className="flex items-center bg-[white] p-6 md:px-24 text-md text-indigo-900 font-semibold">
                <div className="flex w-full max-w-5xl items-center justify-center space-x-4">
                  <div className="h-8 w-8 flex items-center">
                    <svg className="w-6 h-6" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 64 64">
                      <g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <g> <g> <ellipse cx="30.336" cy="12.097" rx="11.997" ry="12.097"></ellipse> <path d="M35.64,30.079H25.031c-7.021,0-12.714,5.739-12.714,12.821v17.771h36.037V42.9 C48.354,35.818,42.661,30.079,35.64,30.079z"></path> </g> </g> </g>
                    </svg>
                  </div>
                  <div className="w-full">{entry.input}</div>
                </div>
              </div>
              {/* <p>AI: {entry.response}</p> */}
              <div className="flex items-center space-x-4 bg-[#ffffff] p-6 md:px-24 text-md text-indigo-900 italic">
                  <div className="flex w-full max-w-5xl items-start justify-center space-x-4">
                    <div className="h-10 w-10 flex items-center rounded-full bg-black/50">
                      <img
                        src="/joseph.jpeg"
                        alt="joseph bailey"
                        className="rounded-full"
                      />
                    </div>
                    <div className="w-full">{entry.response}</div>
                  </div>
                </div>
            </div>
          )
        })}
      </div>
      <div className="absolute bottom-0 flex w-screen flex-col items-center justify-center bg-indigo-900">
        <form className="w-full flex justify-center" onSubmit={(e) => {
          e.preventDefault();
          handleMessage({ message });
          setMessage("");
        }}>
          <input
            name="message"
            className="my-3 w-5/6 rounded border-none bg-white px-4 py-2 text-sm text-black shadow-xl outline-none focus:outline-1 focus:outline-slate-200"
            placeholder="Send a message..."
            value={message}
            onChange={(e) => setMessage(e.target.value)}
          />
          <button className="absolute right-5 top-0 flex h-full items-center justify-center gap-2 hover:text-blue-500">
            Send
            <svg className="w-6 h-6" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 64 64">
              <polygon points="20.9 30.6 21.7 56.4 64 9.5"></polygon><polygon points="0 11.2 20.1 28.7 63.3 7.6"></polygon>
            </svg>
          </button>
        </form>
      </div>
      {/* <button onClick={async () => {
        const messages = await similarMessages({input: "What is your least favorite color?"});
        console.log(messages);
      }}>
        CLICK ME IF YOU DARE
      </button> */}
      {/* <button onClick={async () => {
        insertMessages({ fromTarget: false, content: "👋", timeStamp: BigInt(6)})
      }}>
        INSERT MESSAGE
      </button> */}
    </main>
  );
}

```

### src/app/test/page.tsx

```typescript
"use client"

import { SetStateAction, useState } from "react";
import Link from 'next/link';
import './test.css'

const Test = () => {
  const [activeTab, setActiveTab] = useState('text');

  const handleTabChange = (tab: SetStateAction<string>) => {
    setActiveTab(tab);
  };

  return (
    <div>
      <div className="tab-container">
        <div
          className={`tab ${activeTab === 'text' ? 'active' : ''}`}
          onClick={() => handleTabChange('text')}
        >
          Text
        </div>
        <Link href="/ ">
          <img src="/logo.png" alt="Logo" className="logo" />
        </Link>
        <div
          className={`tab ${activeTab === 'speak' ? 'active' : ''}`}
          onClick={() => handleTabChange('speak')}
        >
          Speak
        </div>
      </div>
      <div className="tab-content">
        {activeTab === 'text' && <input type="text"  className="text-input" placeholder="Ask Joseph Something..."></input>}
        {activeTab === 'speak' && <input type="text" className="text-input" placeholder="Ask Joseph Something..."></input>}
      </div>
      <div className="copyright">
        &copy; 2023 ReVision. All rights reserved.
      </div>
      <div className="copyright">
        &copy; 2023 ReVision. All rights reserved.
      </div>
    </div>
  );
};


export default Test;

```

### src/app/addshit/page.tsx

```typescript
"use client"

import { useAction } from "convex/react";
import { api } from "../../../convex/_generated/api";

export default function AddShit() {
    const insertMessages = useAction(api.realMessage.handleMessageSubmit);
    interface Message {
        Date: number;
        fromTarget: boolean;
        content: string;
        timeStamp: number;
    }
    
    function parseCSV(csvData: string): Message[] {
        const lines = csvData.trim().split('\n').slice(1); // remove header and split into lines
        const messages: Message[] = [];
    
        for (const line of lines) {
            const [_, dateStr, phoneNumberStr, message, orderStr] = line.split(',');
    
            const messageObj: Message = {
                Date: parseFloat(dateStr),
                fromTarget: phoneNumberStr.trim() === "True",
                content: message,
                timeStamp: parseInt(orderStr)
            };
    
            messages.push(messageObj);
        }
    
        return messages;
    }
    
    // Example usage
    const csvData = `,Date,Phone Number,Message,Order
    2,2208122224.0,True,o shit wrong person,3
    3,2208122224.0,True,ignore that plz,4
    4,2208122319.0,False,ok haha im better @ mancala anyways,5
    5,2208122353.0,True,"oh nooo, I’m horrible at mancala",6
    7,2208122353.0,True,just one,8
    16,2208130004.0,False,o no i might lose,17
    17,2208130005.0,True,i can’t tell if that’s sarcasm or not 😅,18
    18,2208130005.0,True,i can’t tell if that’s sarcasm or not 😅,19
    20,2208130006.0,False,no really bc the strategy idk i havent played in a while,21
    22,2208130008.0,False,yea u won..,23
    24,2208130009.0,True,🤭,25
    25,2208130009.0,False,i used to play w aaron n i had win streak of like 20+,26
    26,2208130009.0,False,esp dots n boxes 😋,27
    27,2208130009.0,True,it was on purpose 😃,28
    29,2208130011.0,True,i hate dots n boxes because i have to think so far ahead and it hurts my brain,30
    31,2208130012.0,False,idk i had weird sudoku mancala puzzle phase for a bit so it was rly fun,32
    32,2208130015.0,True,i had a chess phase 🤓,33
    33,2208130016.0,True,i do like puzzles and riddle type stuff tho,34
    34,2208130017.0,False,yea n summer i did like 1000 piece puzzles every week i was rly bored ig,35
    35,2208130019.0,True,you should do them upside down!,36
    36,2208130023.0,False,um. no,37
    37,2208130030.0,False,ohh,38
    38,2208130030.0,False,or mayb ill jus reread pride and prejudice,39
    39,2208130030.0,False,yea i want to read Emma next,40
    40,2208130030.0,False,but it takes so long to comprehend bc the english she uses is so confusing,41
    41,2208130030.0,False,mhm or like your immersed in the reading n then time passes by a lot faster,42
    42,2208130030.0,False,is it like kinda depressing or,43
    43,2208130030.0,False,whats it ab,44
    44,2208130030.0,False,haha ok ill read it later,45
    45,2208130030.0,False,oh that’s interesting,46
    46,2208130030.0,False,i didnt even check the list oopsies,47
    47,2208130030.0,False,the storm?,48
    48,2208130030.0,False,idk i thought id jus finish cuckoo or something,49
    49,2208130030.0,False,oh like a 1000 pc transparent puzzle,50
    50,2208130030.0,False,over summer i read the paris apartment its like this girl whos gonna go live w her brother but she cant find him when she gets to his apartment n she has to figure out if someone killed him or whatever,51
    51,2208130030.0,False,yea i think my favorite genre is romance or mystery,52
    52,2208130030.0,False,ya its basically jus stupid stinky guys hating on women lmao,53
    53,2208130030.0,False,my friend liked the movie n other ppl on letterboxd did too but i fell asleep 🥱,54
    54,2208130030.0,False,like i like the books i read for leisure but they were easier n ive only read half of One Flew Over Cuckoos Nest bc i dont rly like it,55
    55,2208130030.0,False,yea i started reading over summer again bc i was bored but i liked it i jus dont think i chose a good book for ap lit lol,56
    56,2208130030.0,False,shes funny like she seems a little lost but shes trying,57
    57,2208130030.0,False,yea shes so considerate,58
    58,2208130030.0,False,i just need applications tho ill prob turn it in in like 30 mins,59
    59,2208130030.0,False,im not done yet but i sent brandon like 2 ppls work so he did his n sent it to me lmao,60
    60,2208130030.0,False,yea i dont think she cares too much plus she literally told us we could paste pictures of the posters,61
    61,2208130030.0,False,yea im pretty sure bc tonight was the original due date,62
    62,2208130030.0,False,what are u gonna resd for the sem,63
    63,2208130030.0,False,the reading level was kinda like middle school but the plot was really good,64
    64,2208130030.0,False,oh i heard ab persuasion,65
    65,2208130030.0,True,i def wanna read a romance book,66
    66,2208130030.0,True,but then I also want to read a book with someone else so i don’t have to present alone and plus we can talk about the book,67
    67,2208130030.0,True,are you going to read a mystery book for this semester?,68
    68,2208130030.0,True,which mystery books do you like,69
    69,2208130030.0,True,it’s just very agonizing,70
    70,2208130030.0,True,i like them bc it’s like the enemy is nobody,71
    71,2208130030.0,True,you should try romance novels,72
    72,2208130030.0,True,damn,73
    73,2208130030.0,True,i looked at the synopsis for it and I thought it would all take place in a hospital so i didn’t want to read that one,74
    74,2208130030.0,True,lmao,75
    75,2208130030.0,True,no i don’t think it matters too much,76
    76,2208130030.0,True,wdym,77
    77,2208130030.0,True,yeah and I like her plans for us to read short stories,78
    78,2208130030.0,True,yeah lol for some reason i find her personality very amusing,79
    79,2208130030.0,True,i think all she cares about is that we like her class and think she’s a good teacher,80
    80,2208130030.0,True,how long 
[truncated — 35367 more characters]
```

### postcss.config.js

```javascript
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

```

### embeddings.py

```python
from openai.embeddings_utils import get_embedding
import pandas as pd
import openai
import config



openai.api_key = config.OPENAI_API_KEY
df = pd.read_csv("output.csv")
df.pop("Date")
df = df.head(500)
df['Embeddings'] = df["Message"].apply(lambda x: get_embedding(x, engine='text-embedding-ada-002'))
# print(df.iloc[0]["Message"])
# print(get_embedding(df.iloc[0]["Message"], engine='text-embedding-ada-002'))
print(df.head())
df.to_csv("output_embeddings2")

```

### tailwind.config.ts

```typescript
import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      backgroundImage: {
        "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
        "gradient-conic":
          "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
      },
    },
  },
  plugins: [],
};
export default config;

```

### processData.py

```python
import pandas as pd
import re
def processCsv(f_path, numb, output_path):

    # Read the contents of the file
    file_path = f_path
    phone_numbers = []
    messages = []
    dates = []  # List to store dates
    number = numb

    # Regex pattern to match date in the format "Jun 02, 2022 11:19:21 AM"
    date_pattern = re.compile(r'[A-Z][a-z]{2} \d{2}, \d{4} \d{2}:\d{2}:\d{2} [AP]M')

    # Iterate over each line in the file
    with open(file_path, "r", encoding="utf-8", errors="ignore") as file:
        current_date = None  # Initialize variable to store the current date
        for line in file:
            # No need to encode and decode the line again
            line = line.strip()

            # Check if the line contains a date
            match = date_pattern.search(line)
            if match:
                # Update current_date if a date is found
                current_date = match.group()

            # Check if the line contains the catchphrase
            if line.strip() == "Me":
                # Append the current date to dates list
                dates.append(current_date)
                # Get the content from the next line
                next_line = next(file, '').strip()
                messages.append(next_line)
                phone_numbers.append(False)

    # Create a pandas DataFrame
    df = pd.DataFrame({'Date': dates, 'Phone Number': phone_numbers, 'Message': messages})

    phone_numbers = []
    messages = []
    dates = []  # List to store dates

    # Regex pattern to match date in the format "Jun 02, 2022 11:19:21 AM"
    date_pattern = re.compile(r'[A-Z][a-z]{2} \d{2}, \d{4} \d{2}:\d{2}:\d{2} [AP]M')

    # Iterate over each line in the file
    with open(file_path, "r", encoding="utf-8", errors="ignore") as file:
        current_date = None  # Initialize variable to store the current date
        for line in file:
            # No need to encode and decode the line again
            line = line.strip()

            # Check if the line contains a date
            match = date_pattern.search(line)
            if match:
                # Update current_date if a date is found
                current_date = match.group()

            # Check if the line contains the catchphrase
            if line.strip() == number:
                # Append the current date to dates list
                dates.append(current_date)
                # Get the content from the next line
                next_line = next(file, '').strip()
                messages.append(next_line)
                phone_numbers.append(True)

    # Create a pandas DataFrame
    df1 = pd.DataFrame({'Date': dates, 'Phone Number': phone_numbers, 'Message': messages})

    # Assuming df1 and df2 are your two pandas DataFrames
    # Concatenate the two DataFrames
    comb = pd.concat([df, df1], ignore_index=True)

    comb['Date'] = pd.to_datetime(comb['Date'], format='%b %d, %Y %I:%M:%S %p')

    # Convert datetime values to concatenated integer without seconds and first two digits of the year
    comb['Date'] = comb['Date'].dt.strftime('%y%m%d%H%M').astype(float)

    # Sort the combined DataFrame based on the 'Date' category
    comb.sort_values(by='Date', inplace=True)

    # Reset index after sorting
    comb.reset_index(drop=True, inplace=True)

    comb['Order'] = range(1, len(comb) + 1)

    # Print the combined and sorted DataFrame
    comb.to_csv(output_path)

# from flask import Flask, request, jsonify

# app = Flask(__name__)

# @app.route('/run-python-function', methods=['POST'])
# def run_python_function():
#     data = request.json  # Extract input data from JSON body of the HTTP request
#     file_path = data['file_path']  # Access specific input parameter
#     number = data['number']
    
#     # Call your Python function with the received input
#     result = processCsv(file_path, number, "output_file")
    
#     return jsonify(result=result)  # Return result as JSON response

# def your_python_function(input_parameter):
#     # Your Python function logic here
#     return processed_result

# if __name__ == '__main__':
    # app.run(debug=True)

processCsv("/+17737937551.txt", "+17737937551", "output.csv")
```

[13 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]