# Project export: News.ai

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: News.ai makes news accessible to everyone by providing concise, digestible news summaries in a conversational manner.
- Devpost: https://devpost.com/software/news-ai
- GitHub: https://github.com/jcjoshuac/jcjoshuac.github.io
- Demo: https://docs.google.com/presentation/d/1BFEUauRml5CaG0n0nfB_N62Ff-rp_ndb3Bg5vwzOV9w/edit?usp=sharing
- Video: https://www.youtube.com/embed/lirUcGyQZ1c?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — Joshua Chandiramani (4 commits), pruthvi-innamuri (2 commits), Islina Shan (2 commits)

## Devpost submission (written by the team)

### Inspiration

Reading the news helps people expand their knowledge and broaden their horizons. However, it can be time-consuming and troublesome to find quality news articles and read lengthy, boring chunks of text. Our goal is to make news accessible to everyone. We provide concise, digestible news summaries in a conversational manner to make it as easy as possible for anyone to educate themselves by reading the news.

### What it does

News.ai provides a concise and digestible summary of a quality article related to the topic you care about. You can easily ask follow-up questions to learn more information from the article or learn about any related concepts mentioned in the article.

### How we built it

We used React.js and Flask for our web app. We used NewsAPI to recommend the most updated news based on preferences. We used Monster API's OpenAI-Whisper API for speech-to-text transcription. We used Monster API's SunoAI Bark API for text-to-speech generation. We used OpenAI's GPT 4 API large language model (LLM) to provide summaries of news articles.

### Challenges we ran into

We ran into the challenge of connecting multiple parts of the project. Because of its inherent complexity and interconnectivity, making different APIs and frontend plus backend to work together has been our most difficult task.

### Accomplishments we're proud of

We're happy that we established a strong pipeline of API calls using AI models. For example, we converted the user's audio input to text using Whisper API before generating text in response to the user's request using GPT API and finally, we converted the generated text to audio output using Bark API. We are also proud to have integrated the NewsAPI in our recommendation system so we can display the latest news for each user tailored to their preferences.

### What we learned

Each of our team members had a deep understanding of a specific part of our tech stack; whether that be the frontend, backend, or usage of AI/LLM models and APIs. We learned a lot about how these tools can be integrated and applied to solve real-world problems. Furthermore, by spending the first day going booth to booth and speaking individually to every sponsor, we learned about the intricacies of each platform and API. This allowed us to build a platform that synthesized the strengths of various tools and technologies. For example, we were able to take advantage of the ease and scalability of Monster API's Whisper and Bark APIs.

### What's next

Moving forward, we hope to allow for more personalized search of news articles beyond generic topics. Furthermore, we hope to collect additional personalized characteristics that improve the podcast content and understanding for users.

## README (from the GitHub repository)

# News.ai

Description: Web app providing concise, digestible news summaries in a conversational manner <br />

Technologies/Languages: React.js, Flask, NewsAPI, Monster API's OpenAI-Whisper API for speech-to-text transcription, Monster API's SunoAI Bark API for text-to-speech generation, OpenAI's GPT 4 API large language model (LLM) <br />


## Detected evidence (automated analysis)

Indexed codebase: 16 recognized source files, 20 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code
- Flask (technology) — claimed on Devpost, not found in the code
- OpenAI (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (23 of 23)

```
news-digest/.eslintrc.cjs
news-digest/.gitignore
news-digest/index.html
news-digest/monsterAPI/txt2speech.py
news-digest/news-recommendation/news.py
news-digest/package.json
news-digest/public/sample_audio.m4a
news-digest/README.md
news-digest/src/App.css
news-digest/src/App.tsx
news-digest/src/ConversationPage.tsx
news-digest/src/index.css
news-digest/src/main.tsx
news-digest/src/MainPage.tsx
news-digest/src/NewsCategory.tsx
news-digest/src/PodcastPage.tsx
news-digest/src/recommended_news.json
news-digest/src/RecsPage.tsx
news-digest/src/vite-env.d.ts
news-digest/tsconfig.json
news-digest/tsconfig.node.json
news-digest/vite.config.ts
README.md
```

### Dependencies

- news-digest/package.json: @types/react@^18.2.55, @types/react-dom@^18.2.19, @types/react-router-dom@^5.3.3, @typescript-eslint/eslint-plugin@^6.21.0, @typescript-eslint/parser@^6.21.0, @vitejs/plugin-react@^4.2.1, convex@^1.9.0, eslint@^8.56.0, eslint-plugin-react-hooks@^4.6.0, eslint-plugin-react-refresh@^0.4.5, react@^18.2.0, react-dom@^18.2.0, react-router-dom@^6.22.1, typescript@^5.2.2, vite@^5.1.0

### Recent commits (newest first)

- delete index.html
- Update README.md
- Update README.md
- added audio
- Merge branch 'main' of https://github.com/jcjoshuac/jcjoshuac.github.io
- podcast
- Merge branch 'main' of https://github.com/jcjoshuac/jcjoshuac.github.io
- monsterapi
- changed rel to date to sort the news
- Updated the news categories
- recs
- CORS
- removed convex
- added frontend fetch
- Added flask to connect backend to frontend with a server
- updated the io for recommendation system
- Merge pull request #3 from jcjoshuac/islina
- added random categories from enum of categories
- Merge pull request #2 from jcjoshuac/islina
- Added the news recommnedation syste

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

### news-digest/package.json

```
{
  "name": "vite-project",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "@types/react-router-dom": "^5.3.3",
    "convex": "^1.9.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.22.1"
  },
  "devDependencies": {
    "@types/react": "^18.2.55",
    "@types/react-dom": "^18.2.19",
    "@typescript-eslint/eslint-plugin": "^6.21.0",
    "@typescript-eslint/parser": "^6.21.0",
    "@vitejs/plugin-react": "^4.2.1",
    "eslint": "^8.56.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.5",
    "typescript": "^5.2.2",
    "vite": "^5.1.0"
  }
}

```

### news-digest/src/main.tsx

```typescript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css'; // Import global styles here
import App from './App'; // Import the main App component

// Assuming you have an element with the ID 'root' in your index.html
const rootElement = document.getElementById('root');
if (!rootElement) throw new Error('Failed to find the root element');
const root = ReactDOM.createRoot(rootElement);

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

```

### news-digest/src/App.tsx

```typescript
import React, { useState } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import MainPage from './MainPage';
import RecsPage from './RecsPage';
import PodcastPage from './PodcastPage';
import ConversationPage from './ConversationPage';
import './App.css';

const App: React.FC = () => {
  // // Example state variable and setter
  // const [test, setTest] = useState<string>("Hello world");

  // // Example function to handle state change
  // const changeTest = () => {
  //   setTest("Hello Vite + React!");
  // };

  return (
    <Router>
      <Routes>
        <Route path="/" element={<MainPage />} />
        <Route path="/recs" element={<RecsPage />} />
        <Route path="/podcast" element={<PodcastPage />} />
        <Route path="/conversation" element={<ConversationPage />} />
      </Routes>
    </Router>
  );
};

export default App;


```

### news-digest/vite.config.ts

```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    "proxy": {
      "/json_example": 'http://127.0.0.1:5000'
    },
  }
})

```

### news-digest/index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

```

### news-digest/src/vite-env.d.ts

```typescript
/// <reference types="vite/client" />

```

### news-digest/src/NewsCategory.tsx

```typescript
export enum NewsCategory {
    Politics = "Politics",
    Business = "Business",
    Technology = "Technology",
    Education = "Education",
    Entertainment = "Entertainment",
    Sports = "Sports",
    Economy = "Economy",
    Lifestyle = "Lifestyle",
    Travel = "Travel",
    Art = "Art",
    Automotive = "Automotive",
    Culture = "Culture",
    Fashion = "Fashion",
    Food = "Food"
}

```

### news-digest/src/ConversationPage.tsx

```typescript
// ConversationPage.tsx
import React from 'react';
import { useNavigate } from 'react-router-dom';

const ConversationPage: React.FC = () => {
  const navigate = useNavigate();

  const goToMainPage = () => {
    navigate('/');
  };
  const goToRecsPage = () => {
    navigate('/recs');
  };

  return (
    <div>
      <h1>Conversation</h1>
      <button onClick={goToMainPage}>Reselect category</button>
      <button onClick={goToRecsPage}>Reselect news</button>
    </div>
  );
};

export default ConversationPage;

```

### news-digest/src/index.css

```css
:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  font-weight: 400;

  color-scheme: light dark;
  color: rgba(255, 255, 255, 0.87);
  background-color: #242424;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}
a:hover {
  color: #535bf2;
}

body {
  margin: 0;
  display: flex;
  place-items: center;
  min-width: 320px;
  min-height: 100vh;
}

h1 {
  font-size: 3.2em;
  line-height: 1.1;
}

button {
  border-radius: 8px;
  border: 1px solid transparent;
  padding: 0.6em 1.2em;
  font-size: 1em;
  font-weight: 500;
  font-family: inherit;
  background-color: #1a1a1a;
  cursor: pointer;
  transition: border-color 0.25s;
}
button:hover {
  border-color: #646cff;
}
button:focus,
button:focus-visible {
  outline: 4px auto -webkit-focus-ring-color;
}

@media (prefers-color-scheme: light) {
  :root {
    color: #213547;
    background-color: #ffffff;
  }
  a:hover {
    color: #747bff;
  }
  button {
    background-color: #f9f9f9;
  }
}

```

### news-digest/news-recommendation/news.py

```python
# NewsAPI: 81b0c82b-624f-4eea-8ccd-43c53036beff

from eventregistry import *
from flask import Flask, app, request
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

er = EventRegistry(apiKey = '81b0c82b-624f-4eea-8ccd-43c53036beff')

@app.route('/json_example', methods=['POST'])
def handle_json():
    data = request.json
    '''ex json body request
    {
        "user_pref": "Technology"
    }
    '''
    user_pref = data.get('user_pref') # this gets the user pref key from react's fetch request
    recommendations = recommend_news(user_pref)
    return to_json(recommendations)

def recommend_news(user_preference):
    # manually create a new topic page
    topic = TopicPage(er)

    if(er.getCategoryUri(user_preference) != None):
        topic.addCategory(er.getCategoryUri(user_preference), 50)
    elif(er.getConceptUri(user_preference) != None):
        topic.addConcept(er.getConceptUri(user_preference), 50)
    else:
        topic.addKeyword(user_preference, 50)

    # skip articles that are duplicates of other articles
    topic.setArticleHasDuplicateFilter("skipHasDuplicates")
    # return only articles that are about some event that we have detected
    topic.setArticleHasEventFilter("skipArticlesWithoutEvent")
    topic.setLanguages("eng")

    articles = topic.getArticles(page=1, count = 20, sortBy="date")
    return articles


def to_json(recommendations):
    news_data = [{
        "title": article.get("title", "No title available"),
        "content": article.get("body", "No content available"), # or "content" based on the API response
        "link": article.get("url", "No URL available")
    } for article in recommendations.get("articles", {}).get("results", [])]

    json_string = json.dumps(news_data, indent=4)  # 'indent' for pretty-printing
    return json_string

```

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