├── README.md
├── gmail-assistant
├── gmail-assistant-firebase
│ ├── .env.example
│ ├── .gitignore
│ ├── README.md
│ ├── backend
│ │ ├── .env.example
│ │ ├── agent.py
│ │ ├── composio_config.py
│ │ ├── firebase
│ │ │ ├── __pycache__
│ │ │ │ └── init.cpython-312.pyc
│ │ │ └── init.py
│ │ ├── initialize_sheet_agent.py
│ │ ├── main.py
│ │ ├── requirements.txt
│ │ └── setup.sh
│ ├── eslint.config.js
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── public
│ │ └── vite.svg
│ ├── src
│ │ ├── App.css
│ │ ├── App.jsx
│ │ ├── assets
│ │ │ ├── brain.svg
│ │ │ └── react.svg
│ │ ├── components
│ │ │ ├── ActionButton.jsx
│ │ │ ├── AddAgent.jsx
│ │ │ ├── Avatar.jsx
│ │ │ ├── BenefitCard.jsx
│ │ │ ├── Benefits.jsx
│ │ │ ├── FAQ.jsx
│ │ │ ├── Footer.jsx
│ │ │ ├── Hero.jsx
│ │ │ ├── LogoComponent.jsx
│ │ │ ├── Navbar.jsx
│ │ │ ├── ResponsiveMessage.jsx
│ │ │ ├── ScrollToTop.jsx
│ │ │ ├── SettingsAttribute.jsx
│ │ │ ├── SkeletonLoader.jsx
│ │ │ ├── SmallButton.jsx
│ │ │ ├── Working.jsx
│ │ │ └── WorkingFlow.jsx
│ │ ├── config
│ │ │ └── firebase.js
│ │ ├── index.css
│ │ ├── main.jsx
│ │ ├── pages
│ │ │ ├── Agent.jsx
│ │ │ ├── Dashboard.jsx
│ │ │ ├── Home.jsx
│ │ │ ├── Login.jsx
│ │ │ ├── NotFound.jsx
│ │ │ └── Settings.jsx
│ │ └── utils
│ │ │ └── authUtils.js
│ ├── tailwind.config.js
│ ├── vercel.json
│ └── vite.config.js
├── gmail-assistant-simple
│ ├── .gitignore
│ ├── README.md
│ ├── backend
│ │ ├── .env.example
│ │ ├── agent.py
│ │ ├── counter.json
│ │ ├── main.py
│ │ ├── requirements.txt
│ │ ├── setup.sh
│ │ └── taskData.json
│ ├── eslint.config.js
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── public
│ │ └── vite.svg
│ ├── src
│ │ ├── App.css
│ │ ├── App.jsx
│ │ ├── assets
│ │ │ ├── brain.svg
│ │ │ └── react.svg
│ │ ├── components
│ │ │ ├── ActionButton.jsx
│ │ │ ├── BenefitCard.jsx
│ │ │ ├── Benefits.jsx
│ │ │ ├── ConfigParameters.jsx
│ │ │ ├── FAQ.jsx
│ │ │ ├── Footer.jsx
│ │ │ ├── Hero.jsx
│ │ │ ├── LogoComponent.jsx
│ │ │ ├── Navbar.jsx
│ │ │ ├── ScrollToTop.jsx
│ │ │ └── Working.jsx
│ │ ├── index.css
│ │ ├── main.jsx
│ │ └── pages
│ │ │ ├── Dashboard.jsx
│ │ │ └── Home.jsx
│ ├── tailwind.config.js
│ └── vite.config.js
└── gmail-assistant-support
│ ├── .env.example
│ ├── .gitignore
│ ├── README.md
│ ├── backend
│ ├── .env.example
│ ├── agent.py
│ ├── agent_slackbot.py
│ ├── composio_config.py
│ ├── firebase
│ │ ├── __pycache__
│ │ │ └── init.cpython-312.pyc
│ │ └── init.py
│ ├── initialise_agent.py
│ ├── initialize_sheet_agent.py
│ ├── main.py
│ ├── prompt.py
│ ├── requirements.txt
│ └── setup.sh
│ ├── eslint.config.js
│ ├── index.html
│ ├── package-lock.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── public
│ └── vite.svg
│ ├── src
│ ├── App.css
│ ├── App.jsx
│ ├── assets
│ │ ├── brain.svg
│ │ ├── gmailLogo.png
│ │ ├── react.svg
│ │ └── slackLogo.png
│ ├── components
│ │ ├── ActionButton.jsx
│ │ ├── AddAgent.jsx
│ │ ├── Avatar.jsx
│ │ ├── BenefitCard.jsx
│ │ ├── Benefits.jsx
│ │ ├── FAQ.jsx
│ │ ├── Footer.jsx
│ │ ├── Hero.jsx
│ │ ├── LogoComponent.jsx
│ │ ├── Navbar.jsx
│ │ ├── NewMail.jsx
│ │ ├── RedirectedTo.jsx
│ │ ├── ResponsiveMessage.jsx
│ │ ├── ScrollToTop.jsx
│ │ ├── SettingsAttribute.jsx
│ │ ├── SkeletonLoader.jsx
│ │ ├── SmallButton.jsx
│ │ ├── Working.jsx
│ │ └── WorkingFlow.jsx
│ ├── config
│ │ └── firebase.js
│ ├── index.css
│ ├── main.jsx
│ ├── pages
│ │ ├── Agent.jsx
│ │ ├── Home.jsx
│ │ ├── Login.jsx
│ │ ├── NotFound.jsx
│ │ └── Settings.jsx
│ └── utils
│ │ └── authUtils.js
│ ├── tailwind.config.js
│ ├── vercel.json
│ └── vite.config.js
├── imgs
├── banner.gif
├── banner.png
├── composio_black_font.svg
└── composio_white_font.svg
└── twitter-assistant
└── twitter-retweet-helper
├── .env.example
├── .gitignore
├── README.md
├── backend
├── .env.example
├── TweetAndRepost.py
├── agent.py
├── agent_repost.py
├── agent_tweet.py
├── composio_config.py
├── firebase
│ └── init.py
├── get_connections.py
├── initialise_agent.py
├── main.py
├── new_tweet_repost.py
├── prompt.py
├── quote_generator.py
├── repost_existing_tweet.py
├── requirements.txt
├── setup.sh
└── twitter_functions.py
├── eslint.config.js
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
└── vite.svg
├── src
├── App.css
├── App.jsx
├── assets
│ ├── brain.svg
│ ├── gmailLogo.png
│ ├── react.svg
│ ├── slackLogo.png
│ └── twitterUI
│ │ ├── main_post.png
│ │ ├── repost1.png
│ │ └── repost2.png
├── components
│ ├── ActionButton.jsx
│ ├── AddNewUser.jsx
│ ├── AddNewUserOutline.jsx
│ ├── AddNewUserSearchBar.jsx
│ ├── AddedUsers.jsx
│ ├── AuthorizedUsers.jsx
│ ├── Avatar.jsx
│ ├── BenefitCard.jsx
│ ├── Benefits.jsx
│ ├── Clipboard.jsx
│ ├── ComingSoon.jsx
│ ├── CreatePostTextArea.jsx
│ ├── FAQ.jsx
│ ├── Footer.jsx
│ ├── GetPostTextArea.jsx
│ ├── Hero.jsx
│ ├── LogoComponent.jsx
│ ├── Navbar.jsx
│ ├── NewMail.jsx
│ ├── ReTweetCard.jsx
│ ├── RedirectedTo.jsx
│ ├── ResponsiveMessage.jsx
│ ├── ScrollToTop.jsx
│ ├── Separator.jsx
│ ├── SettingsAttribute.jsx
│ ├── SkeletonLoader.jsx
│ ├── SmallButton.jsx
│ ├── TwitterUserCard.jsx
│ ├── UsersIcon.jsx
│ ├── Working.jsx
│ └── WorkingFlow.jsx
├── config
│ └── firebase.js
├── index.css
├── main.jsx
├── pages
│ ├── CreatePost.jsx
│ ├── Home.jsx
│ ├── Login.jsx
│ ├── NotFound.jsx
│ ├── Repost.jsx
│ └── Settings.jsx
└── utils
│ ├── composio_utils.js
│ ├── firebase_utils.js
│ └── twitter_utils.js
├── tailwind.config.js
├── vercel.json
└── vite.config.js
/gmail-assistant/gmail-assistant-firebase/.env.example:
--------------------------------------------------------------------------------
1 | VITE_FIREBASE_API_KEY=
2 | VITE_FIREBASE_AUTH_DOMAIN=
3 | VITE_FIREBASE_PROJECT_ID=
4 | VITE_FIREBASE_STORAGE_BUCKET=
5 | VITE_FIREBASE_MESSAGING_SENDER_ID=
6 | VITE_FIREBASE_APP_ID=
7 | VITE_FIREBASE_MEASUREMENT_ID=
8 | VITE_BACKEND_URL=
9 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | .DS_Store
11 | .env
12 | backend/firebase/genius-57d8d-firebase-adminsdk-ue7u9-90656332c6.json
13 | backend/attachments
14 |
15 | backend/__pycache__
16 | backend/genius-57d8d-firebase-adminsdk-ue7u9-90656332c6.json
17 |
18 | node_modules
19 | dist
20 | dist-ssr
21 | *.local
22 |
23 | # Editor directories and files
24 | .vscode/*
25 | !.vscode/extensions.json
26 | .idea
27 | .DS_Store
28 | *.suo
29 | *.ntvs*
30 | *.njsproj
31 | *.sln
32 | *.sw?
33 |
34 | backend/.env
35 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/backend/.env.example:
--------------------------------------------------------------------------------
1 | OPENAI_API_KEY=
2 | MODEL=
3 | NANO_API_KEY=
4 | NANO_URL=
5 | FRONTEND_URL=
6 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/backend/firebase/__pycache__/init.cpython-312.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ComposioHQ/cookbook/af3a242220f6435c957bd8542ab7bf7e2ec89ab2/gmail-assistant/gmail-assistant-firebase/backend/firebase/__pycache__/init.cpython-312.pyc
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/backend/firebase/init.py:
--------------------------------------------------------------------------------
1 | import firebase_admin
2 | from firebase_admin import credentials, auth, firestore
3 | from pathlib import Path
4 | import os
5 |
6 | # cred = credentials.Certificate(f"{Path.cwd()}/firebase/genius-57d8d-firebase-adminsdk-ue7u9-90656332c6.json")
7 | creds = {
8 | "type": os.environ.get("type"),
9 | "project_id": os.environ.get("project_id"),
10 | "private_key_id": os.environ.get("private_key_id"),
11 | "private_key": os.environ.get("private_key"),
12 | "client_email": os.environ.get("client_email"),
13 | "client_id": os.environ.get("client_id"),
14 | "auth_uri": os.environ.get("auth_uri"),
15 | "token_uri": os.environ.get("token_uri"),
16 | "auth_provider_x509_cert_url":
17 | os.environ.get("auth_provider_x509_cert_url"),
18 | "client_x509_cert_url": os.environ.get("client_x509_cert_url"),
19 | }
20 | firebase_admin.initialize_app(cred)
21 |
22 | db = firestore.client()
23 |
24 | # def get_user_by_username(username):
25 | # users_ref = db.collection('users')
26 | # query = users_ref.where('username', '==', username).limit(1)
27 | # docs = query.get()
28 |
29 | # for doc in docs:
30 | # return doc.to_dict()
31 |
32 | # return False
33 |
34 |
35 | def get_user_by_username(username):
36 | users_ref = db.collection('users')
37 | query = users_ref.where('uid', '==', username).limit(1)
38 | docs = query.get()
39 |
40 | for doc in docs:
41 | return doc.to_dict()
42 |
43 | return False
44 |
45 |
46 | def update_row(uid, new_row):
47 | users_ref = db.collection('users')
48 | query = users_ref.where('uid', '==', uid).limit(1)
49 | docs = query.get()
50 |
51 | for doc in docs:
52 | try:
53 | doc.reference.update({'sheetsConfig.row': str(new_row)})
54 | return True
55 | except Exception as e:
56 | print(f"Error updating user row: {e}")
57 | return False
58 |
59 | print(f"User with uid {uid} not found")
60 | return False
61 |
62 |
63 | def update_spreadsheet_id(username: str, spreadsheet_id: str):
64 | users_ref = db.collection('users')
65 | query = users_ref.where('username', '==', username).limit(1)
66 | docs = query.get()
67 |
68 | for doc in docs:
69 | try:
70 | doc.reference.update(
71 | {'sheetsConfig.spreadsheet_id': spreadsheet_id})
72 | print(f"Successfully updated spreadsheet_id for user {username}")
73 | return True
74 | except Exception as e:
75 | print(f"Error updating spreadsheet_id for user {username}: {e}")
76 | return False
77 |
78 | print(f"User {username} not found")
79 | return False
80 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/backend/requirements.txt:
--------------------------------------------------------------------------------
1 | crewai
2 | composio-crewai
3 | langchain-openai
4 | python-dotenv
5 | crewai_tools
6 | fastapi
7 | uvicorn
8 | langchain_google_genai
9 |
10 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/backend/setup.sh:
--------------------------------------------------------------------------------
1 |
2 | #!/bin/bash
3 |
4 | # Create a virtual environment
5 | echo "Creating virtual environment..."
6 | python3 -m venv ~/.venvs/gmail_agent
7 |
8 | # Activate the virtual environment
9 | echo "Activating virtual environment..."
10 | source ~/.venvs/gmail_agent/bin/activate
11 |
12 | # Install libraries from requirements.txt
13 | echo "Installing libraries from requirements.txt..."
14 | pip install -r requirements.txt
15 |
16 | # Login to your account
17 | echo "Login to your Composio acount"
18 | composio login
19 |
20 | # Add calendar tool
21 | echo "Add Gmail tools. Finish the flow"
22 | composio add gmail
23 | composio add googlesheets
24 |
25 | # Copy env backup to .env file
26 | if [ -f ".env.example" ]; then
27 | echo "Copying .env.example to .env..."
28 | cp .env.example .env
29 | else
30 | echo "No .env.example file found. Creating a new .env file..."
31 | touch .env
32 | fi
33 |
34 | # Prompt user to fill the .env file
35 | echo "Please fill in the .env file with the necessary environment variables."
36 |
37 | echo "Setup completed successfully!"
38 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import react from 'eslint-plugin-react'
4 | import reactHooks from 'eslint-plugin-react-hooks'
5 | import reactRefresh from 'eslint-plugin-react-refresh'
6 |
7 | export default [
8 | { ignores: ['dist'] },
9 | {
10 | files: ['**/*.{js,jsx}'],
11 | languageOptions: {
12 | ecmaVersion: 2020,
13 | globals: globals.browser,
14 | parserOptions: {
15 | ecmaVersion: 'latest',
16 | ecmaFeatures: { jsx: true },
17 | sourceType: 'module',
18 | },
19 | },
20 | settings: { react: { version: '18.3' } },
21 | plugins: {
22 | react,
23 | 'react-hooks': reactHooks,
24 | 'react-refresh': reactRefresh,
25 | },
26 | rules: {
27 | ...js.configs.recommended.rules,
28 | ...react.configs.recommended.rules,
29 | ...react.configs['jsx-runtime'].rules,
30 | ...reactHooks.configs.recommended.rules,
31 | 'react/jsx-no-target-blank': 'off',
32 | 'react-refresh/only-export-components': [
33 | 'warn',
34 | { allowConstantExport: true },
35 | ],
36 | },
37 | },
38 | ]
39 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | GmailGenius
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gmailgenius",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "axios": "^1.7.5",
14 | "firebase": "^10.13.0",
15 | "flowbite": "^2.5.1",
16 | "flowbite-react": "^0.10.1",
17 | "lucide-react": "^0.436.0",
18 | "notistack": "^3.0.1",
19 | "react": "^18.3.1",
20 | "react-dom": "^18.3.1",
21 | "react-loader-spinner": "^6.1.6"
22 | },
23 | "devDependencies": {
24 | "@eslint/js": "^9.9.0",
25 | "@types/react": "^18.3.3",
26 | "@types/react-dom": "^18.3.0",
27 | "@vitejs/plugin-react": "^4.3.1",
28 | "autoprefixer": "^10.4.20",
29 | "eslint": "^9.9.0",
30 | "eslint-plugin-react": "^7.35.0",
31 | "eslint-plugin-react-hooks": "^5.1.0-rc.0",
32 | "eslint-plugin-react-refresh": "^0.4.9",
33 | "globals": "^15.9.0",
34 | "postcss": "^8.4.42",
35 | "react-router-dom": "^6.26.1",
36 | "tailwindcss": "^3.4.10",
37 | "vite": "^5.4.1"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
2 | import { onAuthStateChanged } from "firebase/auth";
3 | import { auth } from "./config/firebase";
4 | import Navbar from "./components/Navbar";
5 | import Home from "./pages/Home";
6 | import Footer from "./components/Footer";
7 | // import Dashboard from "./pages/Dashboard";
8 | import ScrollToTop from "./components/ScrollToTop";
9 | import { useState, useEffect } from "react";
10 | import Login from "./pages/Login";
11 | import Settings from "./pages/Settings";
12 | import Agent from "./pages/Agent";
13 | import NotFound from "./pages/NotFound";
14 | import SkeletonLoader from "./components/SkeletonLoader";
15 | import { SnackbarProvider } from 'notistack'
16 |
17 | const ProtectedRoute = ({ user, children }) => {
18 | if (!user) {
19 | return ;
20 | }
21 | return children;
22 | };
23 |
24 | const App = () => {
25 | const [user, setUser] = useState(null);
26 | const [loading, setLoading] = useState(true);
27 |
28 | useEffect(() => {
29 | const unsubscribe = onAuthStateChanged(auth, (user) => {
30 | setUser(user);
31 | setLoading(false);
32 | });
33 |
34 | return () => unsubscribe();
35 | }, []);
36 |
37 | if (loading) {
38 | return
39 | }
40 |
41 | return (
42 |
43 |
44 |
45 |
46 |
47 | } />
48 | {/*
50 |
51 |
52 | } /> */}
53 |
55 |
56 |
57 | } />
58 | {/*
62 |
63 |
64 | }
65 | /> */}
66 | } />
67 | } />
68 |
69 |
70 |
71 |
72 | );
73 | }
74 |
75 | export default App;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/ActionButton.jsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import { signUpWithGoogle } from "../utils/authUtils";
3 | import { auth } from "../config/firebase";
4 | import { useEffect, useState } from "react";
5 |
6 | const ActionButton = ({ displayName }) => {
7 | const navigate = useNavigate();
8 | const [user, setUser] = useState(null);
9 |
10 | useEffect(() => {
11 | const unsubscribe = auth.onAuthStateChanged((user) => {
12 | setUser(user);
13 | });
14 |
15 | return () => unsubscribe();
16 | }, []);
17 |
18 | const handleClick = async () => {
19 | if (user) {
20 | navigate("/settings");
21 | } else {
22 | try {
23 | await signUpWithGoogle(navigate);
24 | } catch (error) {
25 | console.error("Error during sign up:", error);
26 | }
27 | }
28 | };
29 |
30 | return (
31 |
35 | {displayName}
36 |
37 |
38 | );
39 | };
40 |
41 | export default ActionButton;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/Avatar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { Dropdown } from 'flowbite';
3 | import { logOut } from '../config/firebase';
4 | import { Link } from 'react-router-dom';
5 | const Avatar = ({ user }) => {
6 | useEffect(() => {
7 | if (user) {
8 | const $targetEl = document.getElementById('userDropdown');
9 | const $triggerEl = document.getElementById('avatarButton');
10 |
11 | if ($targetEl && $triggerEl) {
12 | new Dropdown($targetEl, $triggerEl, {
13 | placement: 'bottom-start',
14 | triggerType: 'click'
15 | });
16 | }
17 | }
18 | }, [user]);
19 |
20 | if (!user) return null;
21 |
22 | return (
23 |
24 |
32 |
33 |
34 |
{user.displayName}
35 |
{user.email}
36 |
37 |
38 | {/*
39 | Dashboard
40 | */}
41 | {/*
42 | Add Agent
43 | */}
44 |
45 | Home
46 |
47 |
48 | Settings
49 |
50 |
51 |
54 |
55 |
56 | )
57 | }
58 |
59 | export default Avatar;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/BenefitCard.jsx:
--------------------------------------------------------------------------------
1 | const BenefitCard = ({ title, body }) => {
2 | return (
3 |
7 |
8 | {title}
9 |
10 |
11 | {body}
12 |
13 |
14 | );
15 | };
16 |
17 | export default BenefitCard;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/Benefits.jsx:
--------------------------------------------------------------------------------
1 | import BenefitCard from "./BenefitCard";
2 |
3 | const benefits = [
4 | {
5 | "title": "Time-Saving",
6 | "body": "Eliminate the need for manual email searching and attachment management, freeing up valuable time for other tasks"
7 | },
8 | {
9 | "title": "Improved Productivity",
10 | "body": "Focus on important projects while Attachments Extractor handles the data extraction process, boosting overall productivity"
11 | },
12 | {
13 | "title": "Enhanced Organization",
14 | "body": "Keep valuable information neatly organized in a spreadsheet, making it easy to access and reference whenever needed"
15 | },
16 | {
17 | "title": "Informed Decision Making",
18 | "body": "Quickly access key data extracted from attachments to make well-informed decisions that drive success"
19 | }
20 | ]
21 |
22 | const Benefits = () => {
23 | return
24 |
Benefits
25 |
26 | {
27 | benefits.map((benefit, idx) =>
28 |
29 | )
30 | }
31 |
32 |
33 | }
34 |
35 | export default Benefits;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/Footer.jsx:
--------------------------------------------------------------------------------
1 | import LogoComponent from "./LogoComponent";
2 | const Footer = () => {
3 | return
4 |
5 |
40 |
41 |
42 |
© 2024 GmailGenius | Made with ❤️ by Composio
43 |
44 |
45 |
46 |
47 | }
48 |
49 | export default Footer;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/Hero.jsx:
--------------------------------------------------------------------------------
1 | import ActionButton from "./ActionButton";
2 | import WorkingFlow from "./WorkingFlow";
3 |
4 | const Hero = () => {
5 | return <>
6 | ⚡️Supercharge your Gmail
7 | Automatically processes new emails, extracts data from attachments, and organizes everything in a spreadsheet!
8 |
9 |
12 |
13 | >
14 | }
15 |
16 | export default Hero;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/LogoComponent.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom"
2 | import Logo from "../assets/brain.svg";
3 | const LogoComponent = () => {
4 | return
5 |
6 | GmailGenius
7 |
8 | }
9 |
10 | export default LogoComponent;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import { logOut } from "../config/firebase";
2 | import { Link } from "react-router-dom";
3 | import Avatar from "./Avatar";
4 | import LogoComponent from "./LogoComponent";
5 | const Navbar = ({ user }) => {
6 | return
7 |
19 |
20 |
21 | }
22 |
23 | export default Navbar;
24 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/ResponsiveMessage.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ResponsiveMessage = () => {
4 | return (
5 |
6 |
7 |
This page looks better on Desktop
8 |
Please view on a larger screen for the best experience.
9 |
10 |
11 | );
12 | };
13 |
14 | export default ResponsiveMessage;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/ScrollToTop.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useLocation } from "react-router-dom";
3 |
4 | function ScrollToTop() {
5 | const { pathname } = useLocation();
6 |
7 | useEffect(() => {
8 | window.scrollTo(0, 0);
9 | }, [pathname]);
10 |
11 | return null;
12 | }
13 |
14 | export default ScrollToTop;
15 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/SettingsAttribute.jsx:
--------------------------------------------------------------------------------
1 | import SmallButton from "./SmallButton";
2 | import { Audio } from 'react-loader-spinner'
3 | const SettingsAttribute = ({ type, displayName, value, linkAction, loading, buttonName="Link", showButton=true, textArea=false, onChangeFunction }) => {
4 | return
5 | {displayName}:
6 | {textArea ? : }
19 | {showButton && : buttonName}
29 | action={linkAction}
30 | />}
31 |
32 | }
33 |
34 | export default SettingsAttribute;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/SkeletonLoader.jsx:
--------------------------------------------------------------------------------
1 | const SkeletonLoader = () => {
2 | return
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | };
20 |
21 | export default SkeletonLoader
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/SmallButton.jsx:
--------------------------------------------------------------------------------
1 | const SmallButton = ({ name, action, color, width = "5rem" }) => {
2 | const handleClick = () => {
3 | console.log("button clicked");
4 | }
5 | return
10 | {name}
11 |
12 | }
13 |
14 | export default SmallButton;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/components/Working.jsx:
--------------------------------------------------------------------------------
1 | const Working = () => {
2 | return
3 |
How does this work?
4 |
5 |
6 |
7 | Get Started: Connect your Gmail & Google Sheets and enable the trigger
8 |
9 |
10 | Configure GmailGenius: Add keywords and attributes you want to track
11 |
12 |
13 | Automatic Processing: When a new email arrives, GmailGenius checks for your configured keywords
14 |
15 |
16 | Information Extraction: If keywords are found, GmailGenius extracts information from both the email content and attachments
17 |
18 |
19 | Data Storage: Extracted information is stored in your connected Google Sheets
20 |
21 |
22 |
23 |
24 | }
25 |
26 | export default Working;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 | import ResponsiveMessage from './components/ResponsiveMessage.jsx'
6 |
7 | createRoot(document.getElementById('root')).render(
8 |
9 |
10 |
11 | ,
12 | )
13 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/pages/Agent.jsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import AddAgent from "../components/AddAgent";
3 | import { useEffect, useState } from "react";
4 |
5 | const Agent = ({ user }) => {
6 | const navigate = useNavigate();
7 | const [isLoading, setIsLoading] = useState(true);
8 |
9 | return (
10 |
11 |
12 |
Enter Keywords (Crisp & Concise)
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | export default Agent;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import Hero from "../components/Hero";
2 | import Benefits from "../components/Benefits";
3 | import FAQ from "../components/FAQ";
4 | import Working from "../components/Working";
5 | import ActionButton from "../components/ActionButton";
6 | const Home = () => {
7 | return
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 | }
19 |
20 | export default Home;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import { signUpWithGoogle } from "../utils/authUtils";
3 |
4 | const Login = () => {
5 | const navigate = useNavigate();
6 |
7 | const handleGoogleSignUp = () => {
8 | signUpWithGoogle(navigate);
9 | };
10 |
11 | return (
12 |
13 |
14 |
You're logged out, login below
15 |
18 |
19 |
20 |
23 |
26 |
29 |
32 |
33 |
34 |
35 | Login In with Google
36 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | export default Login;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/pages/NotFound.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | const NotFound = () => {
5 | return (
6 |
7 |
8 |
404
9 |
Something's missing.
10 |
Sorry, we can't find that page.
11 |
Back to Homepage
12 |
13 |
14 | );
15 | };
16 |
17 | export default NotFound;
18 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/src/utils/authUtils.js:
--------------------------------------------------------------------------------
1 | import { signInWithPopup, GoogleAuthProvider } from "firebase/auth";
2 | import { auth } from "../config/firebase";
3 | import { addUserData } from "../config/firebase";
4 |
5 | export const signUpWithGoogle = async (navigate) => {
6 | const provider = new GoogleAuthProvider();
7 | try {
8 | const result = await signInWithPopup(auth, provider);
9 | const user = result.user;
10 | await addUserData(user.uid, user.email.split("@")[0], user.email);
11 | navigate("/settings");
12 | } catch (error) {
13 | alert(error);
14 | console.error("Error during Google sign-up:", error);
15 | }
16 | };
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | darkMode: 'class',
4 | theme: {
5 | extend: {
6 | colors: {
7 | primary: {"50":"#faf5ff","100":"#f3e8ff","200":"#e9d5ff","300":"#d8b4fe","400":"#c084fc","500":"#a855f7","600":"#9333ea","700":"#7e22ce","800":"#6b21a8","900":"#581c87","950":"#3b0764"}
8 | }
9 | },
10 | fontFamily: {
11 | 'body': [
12 | 'Inter',
13 | 'ui-sans-serif',
14 | 'system-ui',
15 | '-apple-system',
16 | 'system-ui',
17 | 'Segoe UI',
18 | 'Roboto',
19 | 'Helvetica Neue',
20 | 'Arial',
21 | 'Noto Sans',
22 | 'sans-serif',
23 | 'Apple Color Emoji',
24 | 'Segoe UI Emoji',
25 | 'Segoe UI Symbol',
26 | 'Noto Color Emoji'
27 | ],
28 | 'sans': [
29 | 'Inter',
30 | 'ui-sans-serif',
31 | 'system-ui',
32 | '-apple-system',
33 | 'system-ui',
34 | 'Segoe UI',
35 | 'Roboto',
36 | 'Helvetica Neue',
37 | 'Arial',
38 | 'Noto Sans',
39 | 'sans-serif',
40 | 'Apple Color Emoji',
41 | 'Segoe UI Emoji',
42 | 'Segoe UI Symbol',
43 | 'Noto Color Emoji'
44 | ]
45 | }
46 | },
47 | content: [
48 | "./index.html",
49 | "./src/**/*.{js,ts,jsx,tsx}",
50 | "./node_modules/flowbite-react/lib/esm/**/*.js",
51 | "./node_modules/flowbite/**/*.js"
52 | ],
53 | theme: {
54 | extend: {},
55 | },
56 | plugins: [
57 | require('flowbite/plugin')
58 | ],
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | {"source": "/(.*)", "destination": "/"}
4 | ]
5 | }
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-firebase/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | backend/__pycache__
11 | .DS_Store
12 |
13 | node_modules
14 | dist
15 | dist-ssr
16 | *.local
17 |
18 | # Editor directories and files
19 | .vscode/*
20 | !.vscode/extensions.json
21 | .idea
22 | .DS_Store
23 | *.suo
24 | *.ntvs*
25 | *.njsproj
26 | *.sln
27 | *.sw?
28 |
29 | backend/.env
30 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/README.md:
--------------------------------------------------------------------------------
1 |
2 | # ⚡️GmailGenius: Supercharge your Gmail
3 | *Effortlessly find emails, extract data from attachments, and organize it in a spreadsheet*
4 | ## Demo
5 | [[Latest Demo](https://github.com/user-attachments/assets/780290da-edf4-4ce1-8ce0-1acc5e519d1b)]
6 | [](https://www.loom.com/embed/c1ff7adf304d47259f71fae09e5f738c?sid=2d11ef56-1fed-4c1d-86b4-1cb4dc449a43)
7 |
8 | ## Description
9 | GmailGenius simplifies the process of finding relevant emails, downloading attachments, and extracting key data. Here's how it works:
10 |
11 |
12 |
13 |
14 | 1. **Sign up on GmailGenius** and link your Gmail account and Google Sheet.
15 | 2. **Enter keywords** you want to search for in your Gmail.
16 | 3. **GmailGenius finds emails and attachments** from Gmail that match your keyword criteria.
17 | 4. **Useful information from the attachments is extracted and stored** in your linked Google Sheet.
18 |
19 | ### Under the hood, the AI agent divides the task into multiple steps and executes them:
20 |
21 | 1. **Retrieves emails from Gmail** that match the keyword/phrase criteria.
22 | 2. **Downloads** the relevant attachments.
23 | 3. **Extracts useful attributes** from the attachments using Nanonets.
24 | 4. **Stores** the extracted data in the linked Google Sheet
25 |
26 | ## Tech Stack
27 | - Frontend: ReactJS, Vite, TailwindCSS
28 | - Backend: Python, FastAPI
29 | - AI Agent: CrewAI, Composio, OpenAI
30 |
31 |
32 | ## Run Locally
33 |
34 | Clone the project
35 |
36 | ```bash
37 | git clone https://github.com/ComposioHQ/cookbook.git
38 | ```
39 |
40 | Go to the project directory
41 |
42 | ```bash
43 | cd gmailgenius-attachment-extract-store
44 | ```
45 |
46 | ### Backend
47 |
48 | Go to backend dir & run setup script, this will create a virtual environment & download necessary libraries (Note: if you're unable to execute then grant permisson -> chmod +x setup.sh)
49 | You'll then be prompted to login to **Composio**, link **Gmail** & **Goole Sheets**.
50 | Add API keys in **.env file**
51 |
52 | ```bash
53 | cd backend && ./setup.sh
54 | ```
55 |
56 | Start the server
57 |
58 | ```bash
59 | python main.py
60 | ```
61 |
62 | Start the email monitoring script
63 |
64 | ```bash
65 | python agent.py
66 | ```
67 |
68 | ### Frontend
69 |
70 | Install dependencies
71 |
72 | ```bash
73 | npm install
74 | ```
75 |
76 | Start the server
77 |
78 | ```bash
79 | npm run dev
80 | ```
81 |
82 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/backend/.env.example:
--------------------------------------------------------------------------------
1 | OPENAI_API_KEY=
2 | MODEL=
3 | NANO_API_KEY=
4 | NANO_URL=
5 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/backend/counter.json:
--------------------------------------------------------------------------------
1 | {"counter": 2}
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/backend/main.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI, HTTPException
2 | from fastapi.middleware.cors import CORSMiddleware
3 | from pydantic import BaseModel
4 | import logging
5 | import json
6 | # Set up logging
7 | logging.basicConfig(level=logging.INFO)
8 | logger = logging.getLogger(__name__)
9 |
10 | app = FastAPI()
11 |
12 | app.add_middleware(
13 | CORSMiddleware,
14 | allow_origins=["*"],
15 | allow_credentials=True,
16 | allow_methods=["*"],
17 | allow_headers=["*"],
18 | )
19 |
20 | def writeData(keywords: str, attributes: str, sheetId: str):
21 | data = {
22 | "keywords": keywords,
23 | "attributes": attributes,
24 | "sheetId": sheetId
25 | }
26 |
27 | with open("taskData.json", "w") as file:
28 | json.dump(data, file, indent=4)
29 |
30 | class Message(BaseModel):
31 | emailKeywords: str
32 | attributes: str
33 | sheetId: str
34 |
35 | @app.post("/configparameters")
36 | async def handle_request(message: Message):
37 | try:
38 | logger.info(f"Received request with emailKeywords: {message.emailKeywords} and attributes: {message.attributes}")
39 | writeData(message.emailKeywords, message.attributes, message.sheetId)
40 | logger.info(f"Data written successfully to taskData.json")
41 | return {"message": "Data written successfully"}
42 | except Exception as e:
43 | logger.error(f"Error occurred: {str(e)}")
44 | raise HTTPException(status_code=500, detail=str(e))
45 |
46 | # To start the server: uvicorn main:app --reload
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/backend/setup.sh:
--------------------------------------------------------------------------------
1 |
2 | #!/bin/bash
3 |
4 | # Create a virtual environment
5 | echo "Creating virtual environment..."
6 | python3 -m venv ~/.venvs/gmail_agent
7 |
8 | # Activate the virtual environment
9 | echo "Activating virtual environment..."
10 | source ~/.venvs/gmail_agent/bin/activate
11 |
12 | # Install libraries from requirements.txt
13 | echo "Installing libraries from requirements.txt..."
14 | pip install -r requirements.txt
15 |
16 | echo "Updating composio apps"
17 | composio apps update
18 |
19 | # Login to your account
20 | echo "Login to your Composio acount"
21 | composio login
22 |
23 | # Add calendar tool
24 | echo "Add Gmail tools. Finish the flow"
25 | composio add gmail
26 | composio add googlesheets
27 |
28 | # Copy env backup to .env file
29 | if [ -f ".env.example" ]; then
30 | echo "Copying .env.example to .env..."
31 | cp .env.example .env
32 | else
33 | echo "No .env.example file found. Creating a new .env file..."
34 | touch .env
35 | fi
36 |
37 | # Prompt user to fill the .env file
38 | echo "Please fill in the .env file with the necessary environment variables."
39 |
40 | echo "Setup completed successfully!"
41 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/backend/taskData.json:
--------------------------------------------------------------------------------
1 | {
2 | "keywords": "Invoice, Apple TV",
3 | "attributes": "Invoice date, invoice number, invoice amount, currency",
4 | "sheetId": "1a-Pg_hszVlg8XfxiZK8I3DBhbveIl_hvrhh37PfJZhU"
5 | }
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import react from 'eslint-plugin-react'
4 | import reactHooks from 'eslint-plugin-react-hooks'
5 | import reactRefresh from 'eslint-plugin-react-refresh'
6 |
7 | export default [
8 | { ignores: ['dist'] },
9 | {
10 | files: ['**/*.{js,jsx}'],
11 | languageOptions: {
12 | ecmaVersion: 2020,
13 | globals: globals.browser,
14 | parserOptions: {
15 | ecmaVersion: 'latest',
16 | ecmaFeatures: { jsx: true },
17 | sourceType: 'module',
18 | },
19 | },
20 | settings: { react: { version: '18.3' } },
21 | plugins: {
22 | react,
23 | 'react-hooks': reactHooks,
24 | 'react-refresh': reactRefresh,
25 | },
26 | rules: {
27 | ...js.configs.recommended.rules,
28 | ...react.configs.recommended.rules,
29 | ...react.configs['jsx-runtime'].rules,
30 | ...reactHooks.configs.recommended.rules,
31 | 'react/jsx-no-target-blank': 'off',
32 | 'react-refresh/only-export-components': [
33 | 'warn',
34 | { allowConstantExport: true },
35 | ],
36 | },
37 | },
38 | ]
39 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | GmailGenius
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gmailgenius",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "flowbite": "^2.5.1",
14 | "flowbite-react": "^0.10.1",
15 | "react": "^18.3.1",
16 | "react-dom": "^18.3.1",
17 | "react-loader-spinner": "^6.1.6"
18 | },
19 | "devDependencies": {
20 | "@eslint/js": "^9.9.0",
21 | "@types/react": "^18.3.3",
22 | "@types/react-dom": "^18.3.0",
23 | "@vitejs/plugin-react": "^4.3.1",
24 | "autoprefixer": "^10.4.20",
25 | "eslint": "^9.9.0",
26 | "eslint-plugin-react": "^7.35.0",
27 | "eslint-plugin-react-hooks": "^5.1.0-rc.0",
28 | "eslint-plugin-react-refresh": "^0.4.9",
29 | "globals": "^15.9.0",
30 | "postcss": "^8.4.41",
31 | "react-router-dom": "^6.26.1",
32 | "tailwindcss": "^3.4.10",
33 | "vite": "^5.4.1"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { BrowserRouter, Routes, Route } from "react-router-dom";
2 | import Navbar from "./components/Navbar";
3 | import Home from "./pages/Home";
4 | import Footer from "./components/Footer";
5 | import Dashboard from "./pages/Dashboard";
6 | import ScrollToTop from "./components/ScrollToTop";
7 |
8 | const App = () => {
9 | return <>
10 |
11 |
12 |
13 |
14 | } />
15 | }>
16 |
17 |
18 |
19 |
20 | >
21 | }
22 |
23 | export default App;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/components/ActionButton.jsx:
--------------------------------------------------------------------------------
1 | import {Link} from "react-router-dom";
2 | const ActionButton = ({displayName, link}) => {
3 | return
4 | {displayName}
5 |
6 |
7 | }
8 |
9 | export default ActionButton;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/components/BenefitCard.jsx:
--------------------------------------------------------------------------------
1 | const BenefitCard = ({ title, body }) => {
2 | return (
3 |
7 |
8 | {title}
9 |
10 |
11 | {body}
12 |
13 |
14 | );
15 | };
16 |
17 | export default BenefitCard;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/components/Benefits.jsx:
--------------------------------------------------------------------------------
1 | import BenefitCard from "./BenefitCard";
2 |
3 | const benefits = [
4 | {
5 | "title": "Time-Saving",
6 | "body": "Eliminate the need for manual email searching and attachment management, freeing up valuable time for other tasks"
7 | },
8 | {
9 | "title": "Improved Productivity",
10 | "body": "Focus on important projects while Attachments Extractor handles the data extraction process, boosting overall productivity"
11 | },
12 | {
13 | "title": "Enhanced Organization",
14 | "body": "Keep valuable information neatly organized in a spreadsheet, making it easy to access and reference whenever needed"
15 | },
16 | {
17 | "title": "Informed Decision Making",
18 | "body": "Quickly access key data extracted from attachments to make well-informed decisions that drive success"
19 | }
20 | ]
21 |
22 | const Benefits = () => {
23 | return
24 |
Benefits
25 |
26 | {
27 | benefits.map((benefit, idx) =>
28 |
29 | )
30 | }
31 |
32 |
33 | }
34 |
35 | export default Benefits;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/components/Hero.jsx:
--------------------------------------------------------------------------------
1 | import ActionButton from "./ActionButton";
2 |
3 | const Hero = () => {
4 | return <>
5 | ⚡️Supercharge your Gmail
6 | Automatically processes new emails, extracts data from attachments, and organizes everything in a spreadsheet!
7 |
10 |
11 | >
12 | }
13 |
14 | export default Hero;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/components/LogoComponent.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom"
2 | import Logo from "../assets/brain.svg";
3 | const LogoComponent = () => {
4 | return
5 |
6 | GmailGenius
7 |
8 | }
9 |
10 | export default LogoComponent;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import LogoComponent from "./LogoComponent";
2 | const Navbar = () => {
3 | return
4 |
12 |
13 |
14 | }
15 |
16 | export default Navbar;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/components/ScrollToTop.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useLocation } from "react-router-dom";
3 |
4 | function ScrollToTop() {
5 | const { pathname } = useLocation();
6 |
7 | useEffect(() => {
8 | window.scrollTo(0, 0);
9 | }, [pathname]);
10 |
11 | return null;
12 | }
13 |
14 | export default ScrollToTop;
15 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/components/Working.jsx:
--------------------------------------------------------------------------------
1 | const Working = () => {
2 | return
3 |
How does this work?
4 |
5 |
6 |
7 | Sign up on Composio and link your Gmail & Sheets
8 |
9 |
10 | Enter keywords you want to search for in your Gmail
11 |
12 |
13 | We'll Find Emails & attachments from Gmail that match your keyword criteria
14 |
15 |
16 | Store useful information from attachments in sheets
17 |
18 |
19 |
20 |
21 | }
22 |
23 | export default Working;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 |
6 | createRoot(document.getElementById('root')).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/pages/Dashboard.jsx:
--------------------------------------------------------------------------------
1 | import ConfigParameters from "../components/ConfigParameters";
2 | const Dashboard = () => {
3 | return
4 |
5 | Enter Keywords (Crisp & Concise)
6 |
7 |
8 |
9 | }
10 |
11 | export default Dashboard;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import Hero from "../components/Hero";
2 | import Benefits from "../components/Benefits";
3 | import FAQ from "../components/FAQ";
4 | import Working from "../components/Working";
5 | import ActionButton from "../components/ActionButton";
6 | const Home = () => {
7 | return
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 | }
19 |
20 | export default Home;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | darkMode: 'class',
4 | theme: {
5 | extend: {
6 | colors: {
7 | primary: {"50":"#faf5ff","100":"#f3e8ff","200":"#e9d5ff","300":"#d8b4fe","400":"#c084fc","500":"#a855f7","600":"#9333ea","700":"#7e22ce","800":"#6b21a8","900":"#581c87","950":"#3b0764"}
8 | }
9 | },
10 | fontFamily: {
11 | 'body': [
12 | 'Inter',
13 | 'ui-sans-serif',
14 | 'system-ui',
15 | '-apple-system',
16 | 'system-ui',
17 | 'Segoe UI',
18 | 'Roboto',
19 | 'Helvetica Neue',
20 | 'Arial',
21 | 'Noto Sans',
22 | 'sans-serif',
23 | 'Apple Color Emoji',
24 | 'Segoe UI Emoji',
25 | 'Segoe UI Symbol',
26 | 'Noto Color Emoji'
27 | ],
28 | 'sans': [
29 | 'Inter',
30 | 'ui-sans-serif',
31 | 'system-ui',
32 | '-apple-system',
33 | 'system-ui',
34 | 'Segoe UI',
35 | 'Roboto',
36 | 'Helvetica Neue',
37 | 'Arial',
38 | 'Noto Sans',
39 | 'sans-serif',
40 | 'Apple Color Emoji',
41 | 'Segoe UI Emoji',
42 | 'Segoe UI Symbol',
43 | 'Noto Color Emoji'
44 | ]
45 | }
46 | },
47 | content: [
48 | "./index.html",
49 | "./src/**/*.{js,ts,jsx,tsx}",
50 | 'node_modules/flowbite-react/lib/esm/**/*.js'
51 | ],
52 | theme: {
53 | extend: {},
54 | },
55 | plugins: [
56 | require('flowbite/plugin')
57 | ],
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-simple/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/.env.example:
--------------------------------------------------------------------------------
1 | VITE_FIREBASE_API_KEY=
2 | VITE_FIREBASE_AUTH_DOMAIN=
3 | VITE_FIREBASE_PROJECT_ID=
4 | VITE_FIREBASE_STORAGE_BUCKET=
5 | VITE_FIREBASE_MESSAGING_SENDER_ID=
6 | VITE_FIREBASE_APP_ID=
7 | VITE_FIREBASE_MEASUREMENT_ID=
8 | VITE_BACKEND_URL=
9 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | .env
11 | backend/firebase/support-bot-49f93-94ae307979d3.json
12 | backend/attachments
13 |
14 | backend/__pycache__
15 | backend/genius-57d8d-firebase-adminsdk-ue7u9-90656332c6.json
16 |
17 | node_modules
18 | dist
19 | dist-ssr
20 | *.local
21 |
22 | # Editor directories and files
23 | .vscode/*
24 | !.vscode/extensions.json
25 | .idea
26 | .DS_Store
27 | *.suo
28 | *.ntvs*
29 | *.njsproj
30 | *.sln
31 | *.sw?
32 |
33 | backend/.env
34 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/backend/.env.example:
--------------------------------------------------------------------------------
1 | OPENAI_API_KEY=
2 | MODEL=
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/backend/agent_slackbot.py:
--------------------------------------------------------------------------------
1 | import os
2 | from composio_crewai import Action, ComposioToolSet
3 | from crewai import Agent, Crew, Task, Process
4 | from langchain_openai import ChatOpenAI
5 | from dotenv import load_dotenv
6 | from pathlib import Path
7 |
8 | load_dotenv()
9 |
10 | llm = ChatOpenAI(model="gpt-4o")
11 |
12 | # ComposioToolSet instance
13 | composio_toolset = ComposioToolSet(api_key=os.environ.get("COMPOSIO_API_KEY"))
14 |
15 | def send_slack_message_and_file(message: str, channel: str, file_path: str = None) -> str:
16 | print("Sending Slack message with attachment")
17 |
18 | # Tools
19 | tools = composio_toolset.get_actions(actions=[
20 | Action.SLACKBOT_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL,
21 | Action.SLACKBOT_UPLOAD_OR_CREATE_A_FILE_IN_SLACK
22 | ])
23 |
24 | # Agent
25 | slack_assistant = Agent(
26 | role="Slack Assistant",
27 | goal="Send messages and files to Slack channels",
28 | backstory="You're an AI assistant that sends messages",
29 | verbose=True,
30 | llm=llm,
31 | tools=tools,
32 | allow_delegation=False,
33 | )
34 |
35 | task_description = f"""
36 | 1. Send the following message to the Slack channel '{channel}':
37 | "{message}" along with the file: {file_path}
38 | """
39 |
40 | process_slack_request = Task(
41 | description=task_description,
42 | agent=slack_assistant,
43 | expected_output="Confirmation that the message was sent to the specified Slack channel.",
44 | )
45 |
46 | slack_processing_crew = Crew(
47 | agents=[slack_assistant],
48 | tasks=[process_slack_request],
49 | verbose=1,
50 | process=Process.sequential,
51 | )
52 |
53 | result = slack_processing_crew.kickoff()
54 | return result
55 |
56 | send_slack_message_and_file("Hello, this is a test message with attachment", "dev-channel", "/Users/abhishekpatil/Desktop/cookbook-local-new/gmail-assistant/gmail-assistant-support/backend/attachments/GMAIL_GET_ATTACHMENT_abishkpatil_1725767832.0156858_Screenshot 2024-09-07 at 10.40.59 PM.png")
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/backend/firebase/__pycache__/init.cpython-312.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ComposioHQ/cookbook/af3a242220f6435c957bd8542ab7bf7e2ec89ab2/gmail-assistant/gmail-assistant-support/backend/firebase/__pycache__/init.cpython-312.pyc
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/backend/firebase/init.py:
--------------------------------------------------------------------------------
1 | import firebase_admin
2 | from firebase_admin import credentials, auth, firestore
3 | from pathlib import Path
4 | import os
5 |
6 | cred = credentials.Certificate(f"{Path.cwd()}/firebase/support-bot-49f93-94ae307979d3.json")
7 | # creds = {
8 | # "type": os.environ.get("type"),
9 | # "project_id": os.environ.get("project_id"),
10 | # "private_key_id": os.environ.get("private_key_id"),
11 | # "private_key": os.environ.get("private_key"),
12 | # "client_email": os.environ.get("client_email"),
13 | # "client_id": os.environ.get("client_id"),
14 | # "auth_uri": os.environ.get("auth_uri"),
15 | # "token_uri": os.environ.get("token_uri"),
16 | # "auth_provider_x509_cert_url":
17 | # os.environ.get("auth_provider_x509_cert_url"),
18 | # "client_x509_cert_url": os.environ.get("client_x509_cert_url"),
19 | # }
20 | firebase_admin.initialize_app(cred)
21 |
22 | db = firestore.client()
23 |
24 | # def get_user_by_username(username):
25 | # users_ref = db.collection('users')
26 | # query = users_ref.where('username', '==', username).limit(1)
27 | # docs = query.get()
28 |
29 | # for doc in docs:
30 | # return doc.to_dict()
31 |
32 | # return False
33 |
34 |
35 | def get_user_by_username(username):
36 | users_ref = db.collection('users')
37 | query = users_ref.where('uid', '==', username).limit(1)
38 | docs = query.get()
39 |
40 | for doc in docs:
41 | return doc.to_dict()
42 |
43 | return False
44 |
45 |
46 | def update_row(uid, new_row):
47 | users_ref = db.collection('users')
48 | query = users_ref.where('uid', '==', uid).limit(1)
49 | docs = query.get()
50 |
51 | for doc in docs:
52 | try:
53 | doc.reference.update({'sheetsConfig.row': str(new_row)})
54 | return True
55 | except Exception as e:
56 | print(f"Error updating user row: {e}")
57 | return False
58 |
59 | print(f"User with uid {uid} not found")
60 | return False
61 |
62 |
63 | def update_spreadsheet_id(username: str, spreadsheet_id: str):
64 | users_ref = db.collection('users')
65 | query = users_ref.where('username', '==', username).limit(1)
66 | docs = query.get()
67 |
68 | for doc in docs:
69 | try:
70 | doc.reference.update(
71 | {'sheetsConfig.spreadsheet_id': spreadsheet_id})
72 | print(f"Successfully updated spreadsheet_id for user {username}")
73 | return True
74 | except Exception as e:
75 | print(f"Error updating spreadsheet_id for user {username}: {e}")
76 | return False
77 |
78 | print(f"User {username} not found")
79 | return False
80 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/backend/initialise_agent.py:
--------------------------------------------------------------------------------
1 | import json
2 | from composio_crewai import Action, ComposioToolSet
3 | from crewai import Agent, Crew, Task, Process
4 | from langchain_openai import ChatOpenAI
5 | from dotenv import load_dotenv
6 | from crewai_tools.tools.base_tool import BaseTool
7 | from firebase.init import db
8 | import os
9 |
10 | load_dotenv()
11 | llm = ChatOpenAI(model="gpt-4o")
12 |
13 |
14 | def initialise(entityId: str):
15 | username = entityId
16 |
17 | def get_user_by_username(username):
18 | users_ref = db.collection('users')
19 | query = users_ref.where('username', '==', username).limit(1)
20 | docs = query.get()
21 |
22 | for doc in docs:
23 | return doc.to_dict()
24 |
25 | return False
26 |
27 | user = get_user_by_username(entityId)
28 | keywords = user['keywords'][0]['keywords']
29 | email = user['email']
30 | composio_toolset = ComposioToolSet(
31 | api_key=os.environ.get("COMPOSIO_API_KEY"), entity_id=entityId)
32 |
33 | google_tools = composio_toolset.get_actions(actions=[Action.GMAIL_SEND_EMAIL])
34 |
35 | google_assistant = Agent(
36 | role="Gmail Assistant",
37 | goal= """
38 | 1. Send an email to the user with the specified keywords in the body.
39 | """,
40 | backstory=
41 | "You're an AI assistant that handles Gmail operations using Gmail APIs.",
42 | verbose=True,
43 | llm=llm,
44 | tools=google_tools,
45 | allow_delegation=False,
46 | )
47 |
48 | send_email_task = Task(
49 | description=f"""
50 | Send an email with the following details:
51 | 1. Recipient email: {email}
52 | 2. Subject: "Test email with keywords"
53 | 3. Body: "This is a test email containing the following keywords: {keywords}. These keywords will be used to filter and forward your emails."
54 | Only specify recipient_email, subject, and body when sending the email.
55 | """,
56 | agent=google_assistant,
57 | expected_output=
58 | "Send an email to the user with the specified keywords",
59 | )
60 |
61 | gmail_processing_crew = Crew(
62 | agents=[google_assistant],
63 | tasks=[send_email_task],
64 | verbose=1,
65 | process=Process.sequential,
66 | )
67 | return gmail_processing_crew.kickoff()
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/backend/prompt.py:
--------------------------------------------------------------------------------
1 | prompt1 = f"""
2 | 1. Send an automatic reply to the sender of the email with the following message:
3 | "Thank you for your email. We have received it and will get back to you shortly"
4 | using GMAIL_REPLY_TO_THREAD action & if any attachments are present, use GMAIL_SEND_EMAIL send that too.
5 | 2. Check if the email subject (subject) or body (messageText) in the payload contains any of the keywords specified in this dictionary: {[{'slackChannel': 'dev-channel', 'email': 'hrishikeshvastrad14@gmail.com', 'keywords': 'bugs, errors, issues'}, {'slackChannel': 'growth-channel', 'email': 'mskarthikugalawat@gmail.com', 'keywords': 'Collaboration, partnership, sponser'}, {'slackChannel': 'hrishikesh-channel', 'email': 'hrishikesh@gmail.com', 'keywords': 'bill'}]}.
6 | 3. If a keyword match is found:
7 | a. Check if the original email contains any attachments.
8 | b. If attachments are present, use the GMAIL_GET_ATTACHMENT action to download them.
9 | c. Send the email payload to the corresponding email address and slack channel. And if attachments are present include the downloaded attachments.
10 | message: 'Forwarded email: subject & body'
11 | Payload: {payload}
12 | """
13 | prompt2 = f"""
14 | 1. Check if the email subject (subject) or body (messageText) in the payload contains any of the keywords specified in this dictionary: {keywords}.
15 | 2. If a keyword match is found:
16 | a. Check if the original email contains any attachments.
17 | b. If attachments are present, use the GMAIL_GET_ATTACHMENT action to download them.
18 | c. Send the email payload to the corresponding email address and slack channel. And if attachments are present include the downloaded attachments.
19 | message: 'Forwarded email: subject & body'
20 | Payload: {payload}
21 | """
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/backend/requirements.txt:
--------------------------------------------------------------------------------
1 | crewai
2 | composio-crewai
3 | langchain-openai
4 | python-dotenv
5 | crewai_tools
6 | fastapi
7 | uvicorn
8 | langchain_google_genai
9 | firebase-admin
10 |
11 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/backend/setup.sh:
--------------------------------------------------------------------------------
1 |
2 | #!/bin/bash
3 |
4 | # Create a virtual environment
5 | echo "Creating virtual environment..."
6 | python3 -m venv ~/.venvs/gmail_agent
7 |
8 | # Activate the virtual environment
9 | echo "Activating virtual environment..."
10 | source ~/.venvs/gmail_agent/bin/activate
11 |
12 | # Install libraries from requirements.txt
13 | echo "Installing libraries from requirements.txt..."
14 | pip install -r requirements.txt
15 |
16 | # Login to your account
17 | echo "Login to your Composio acount"
18 | composio login
19 |
20 | # Add calendar tool
21 | echo "Add Gmail tools. Finish the flow"
22 | composio add gmail
23 | composio add googlesheets
24 |
25 | # Copy env backup to .env file
26 | if [ -f ".env.example" ]; then
27 | echo "Copying .env.example to .env..."
28 | cp .env.example .env
29 | else
30 | echo "No .env.example file found. Creating a new .env file..."
31 | touch .env
32 | fi
33 |
34 | # Prompt user to fill the .env file
35 | echo "Please fill in the .env file with the necessary environment variables."
36 |
37 | echo "Setup completed successfully!"
38 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import react from 'eslint-plugin-react'
4 | import reactHooks from 'eslint-plugin-react-hooks'
5 | import reactRefresh from 'eslint-plugin-react-refresh'
6 |
7 | export default [
8 | { ignores: ['dist'] },
9 | {
10 | files: ['**/*.{js,jsx}'],
11 | languageOptions: {
12 | ecmaVersion: 2020,
13 | globals: globals.browser,
14 | parserOptions: {
15 | ecmaVersion: 'latest',
16 | ecmaFeatures: { jsx: true },
17 | sourceType: 'module',
18 | },
19 | },
20 | settings: { react: { version: '18.3' } },
21 | plugins: {
22 | react,
23 | 'react-hooks': reactHooks,
24 | 'react-refresh': reactRefresh,
25 | },
26 | rules: {
27 | ...js.configs.recommended.rules,
28 | ...react.configs.recommended.rules,
29 | ...react.configs['jsx-runtime'].rules,
30 | ...reactHooks.configs.recommended.rules,
31 | 'react/jsx-no-target-blank': 'off',
32 | 'react-refresh/only-export-components': [
33 | 'warn',
34 | { allowConstantExport: true },
35 | ],
36 | },
37 | },
38 | ]
39 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | GmailGenius
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gmailgenius",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "axios": "^1.7.5",
14 | "firebase": "^10.13.0",
15 | "flowbite": "^2.5.1",
16 | "flowbite-react": "^0.10.1",
17 | "lucide-react": "^0.436.0",
18 | "notistack": "^3.0.1",
19 | "react": "^18.3.1",
20 | "react-dom": "^18.3.1",
21 | "react-loader-spinner": "^6.1.6"
22 | },
23 | "devDependencies": {
24 | "@eslint/js": "^9.9.0",
25 | "@types/react": "^18.3.3",
26 | "@types/react-dom": "^18.3.0",
27 | "@vitejs/plugin-react": "^4.3.1",
28 | "autoprefixer": "^10.4.20",
29 | "eslint": "^9.9.0",
30 | "eslint-plugin-react": "^7.35.0",
31 | "eslint-plugin-react-hooks": "^5.1.0-rc.0",
32 | "eslint-plugin-react-refresh": "^0.4.9",
33 | "globals": "^15.9.0",
34 | "postcss": "^8.4.42",
35 | "react-router-dom": "^6.26.1",
36 | "tailwindcss": "^3.4.10",
37 | "vite": "^5.4.1"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
2 | import { onAuthStateChanged } from "firebase/auth";
3 | import { auth } from "./config/firebase";
4 | import Navbar from "./components/Navbar";
5 | import Home from "./pages/Home";
6 | import Footer from "./components/Footer";
7 | import ScrollToTop from "./components/ScrollToTop";
8 | import { useState, useEffect } from "react";
9 | import Login from "./pages/Login";
10 | import Settings from "./pages/Settings";
11 | import NotFound from "./pages/NotFound";
12 | import SkeletonLoader from "./components/SkeletonLoader";
13 | import { SnackbarProvider } from 'notistack'
14 |
15 | const ProtectedRoute = ({ user, children }) => {
16 | if (!user) {
17 | return ;
18 | }
19 | return children;
20 | };
21 |
22 | const App = () => {
23 | const [user, setUser] = useState(null);
24 | const [loading, setLoading] = useState(true);
25 |
26 | useEffect(() => {
27 | const unsubscribe = onAuthStateChanged(auth, (user) => {
28 | setUser(user);
29 | setLoading(false);
30 | });
31 |
32 | return () => unsubscribe();
33 | }, []);
34 |
35 | if (loading) {
36 | return
37 | }
38 |
39 | return (
40 |
41 |
42 |
43 |
44 |
45 | } />
46 |
48 |
49 |
50 | } />
51 | } />
52 | } />
53 |
54 |
55 |
56 |
57 | );
58 | }
59 |
60 | export default App;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/assets/gmailLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ComposioHQ/cookbook/af3a242220f6435c957bd8542ab7bf7e2ec89ab2/gmail-assistant/gmail-assistant-support/src/assets/gmailLogo.png
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/assets/slackLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ComposioHQ/cookbook/af3a242220f6435c957bd8542ab7bf7e2ec89ab2/gmail-assistant/gmail-assistant-support/src/assets/slackLogo.png
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/ActionButton.jsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import { signUpWithGoogle } from "../utils/authUtils";
3 | import { auth } from "../config/firebase";
4 | import { useEffect, useState } from "react";
5 |
6 | const ActionButton = ({ displayName }) => {
7 | const navigate = useNavigate();
8 | const [user, setUser] = useState(null);
9 |
10 | useEffect(() => {
11 | const unsubscribe = auth.onAuthStateChanged((user) => {
12 | setUser(user);
13 | });
14 |
15 | return () => unsubscribe();
16 | }, []);
17 |
18 | const handleClick = async () => {
19 | if (user) {
20 | navigate("/settings");
21 | } else {
22 | try {
23 | await signUpWithGoogle(navigate);
24 | } catch (error) {
25 | console.error("Error during sign up:", error);
26 | }
27 | }
28 | };
29 |
30 | return (
31 |
35 | {displayName}
36 |
37 |
38 | );
39 | };
40 |
41 | export default ActionButton;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/Avatar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { Dropdown } from 'flowbite';
3 | import { logOut } from '../config/firebase';
4 | import { Link } from 'react-router-dom';
5 | const Avatar = ({ user }) => {
6 | useEffect(() => {
7 | if (user) {
8 | const $targetEl = document.getElementById('userDropdown');
9 | const $triggerEl = document.getElementById('avatarButton');
10 |
11 | if ($targetEl && $triggerEl) {
12 | new Dropdown($targetEl, $triggerEl, {
13 | placement: 'bottom-start',
14 | triggerType: 'click'
15 | });
16 | }
17 | }
18 | }, [user]);
19 |
20 | if (!user) return null;
21 |
22 | return (
23 |
24 |
32 |
33 |
34 |
{user.displayName}
35 |
{user.email}
36 |
37 |
38 | {/*
39 | Dashboard
40 | */}
41 | {/*
42 | Add Agent
43 | */}
44 |
45 | Home
46 |
47 |
48 | Settings
49 |
50 |
51 |
54 |
55 |
56 | )
57 | }
58 |
59 | export default Avatar;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/BenefitCard.jsx:
--------------------------------------------------------------------------------
1 | const BenefitCard = ({ title, body }) => {
2 | return (
3 |
7 |
8 | {title}
9 |
10 |
11 | {body}
12 |
13 |
14 | );
15 | };
16 |
17 | export default BenefitCard;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/Benefits.jsx:
--------------------------------------------------------------------------------
1 | import BenefitCard from "./BenefitCard";
2 |
3 | const benefits = [
4 | {
5 | "title": "Automated Email Processing",
6 | "body": "GmailGenius automatically processes new emails, sending acknowledgments to senders and categorizing messages based on user-specified keywords"
7 | },
8 | {
9 | "title": "Intelligent Routing",
10 | "body": "Automatically directs emails to the right team (e.g., development for bugs, growth for collaborations) based on content analysis"
11 | },
12 | {
13 | "title": "Instant Team Notifications",
14 | "body": "Sends relevant messages to appropriate Slack channels, ensuring quick team awareness and response"
15 | },
16 | {
17 | "title": "Time and Resource Optimization",
18 | "body": "Eliminates manual email sorting and forwarding, allowing teams to focus on addressing important matters efficiently"
19 | }
20 | ]
21 |
22 | const Benefits = () => {
23 | return
24 |
Benefits
25 |
26 | {
27 | benefits.map((benefit, idx) =>
28 |
29 | )
30 | }
31 |
32 |
33 | }
34 |
35 | export default Benefits;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/Footer.jsx:
--------------------------------------------------------------------------------
1 | import LogoComponent from "./LogoComponent";
2 | const Footer = () => {
3 | return
4 |
5 |
40 |
41 |
42 |
© 2024 GmailGenius | Made with ❤️ by Composio
43 |
44 |
45 |
46 |
47 | }
48 |
49 | export default Footer;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/Hero.jsx:
--------------------------------------------------------------------------------
1 | import ActionButton from "./ActionButton";
2 | import WorkingFlow from "./WorkingFlow";
3 |
4 | const Hero = () => {
5 | return <>
6 | ⚡️Supercharge your Gmail
7 | Turbocharge your Gmail! GmailGenius scans new emails, instantly zapping important emails to the right team - all while you sip your coffee.
8 |
9 |
12 |
13 | >
14 | }
15 |
16 | export default Hero;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/LogoComponent.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom"
2 | import Logo from "../assets/brain.svg";
3 | const LogoComponent = () => {
4 | return
5 |
6 | GmailGenius
7 |
8 | }
9 |
10 | export default LogoComponent;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import { logOut } from "../config/firebase";
2 | import { Link } from "react-router-dom";
3 | import Avatar from "./Avatar";
4 | import LogoComponent from "./LogoComponent";
5 | const Navbar = ({ user }) => {
6 | return
7 |
19 |
20 |
21 | }
22 |
23 | export default Navbar;
24 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/NewMail.jsx:
--------------------------------------------------------------------------------
1 | import { Mail, Paperclip } from "lucide-react"
2 | const NewMail = () => {
3 | return
4 |
5 |
6 |
Gmail
7 |
8 |
9 |
10 |
11 |
12 |
13 | Subject
14 |
15 |
16 | Message
17 |
18 |
19 |
20 |
21 |
22 | Bug Report: App Crashing on Launch
23 |
24 |
25 | Your app is constantly crashing when I try to open it. This is unacceptable! I've been a loyal user for years and now I can't even...
26 |
27 |
28 |
29 |
30 | {/* Hackathon Collaboration Invitation */}
31 | Hackathon Collaboration Invitation
32 |
33 |
34 | We're hosting a hackathon and would love to have you collaborate with us. Your tools would be...
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | }
43 |
44 | export default NewMail;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/RedirectedTo.jsx:
--------------------------------------------------------------------------------
1 | import gmailLogo from '../assets/gmailLogo.png'
2 | import slackLogo from '../assets/slackLogo.png'
3 | const RedirectedTo = () => {
4 | return
5 |
6 |
7 | Issue
8 | Bug
9 | Glitch
10 | Error
11 |
12 |
13 |
14 |
15 |
devteam@company.com
16 |
17 |
18 |
19 |
@dev-channel
20 |
21 |
22 |
23 |
24 |
25 | Growth
26 | Marketing
27 | Collaboration
28 |
29 |
30 |
31 |
32 |
growth@company.com
33 |
34 |
35 |
36 |
@growth-channel
37 |
38 |
39 |
40 |
41 | }
42 |
43 | export default RedirectedTo;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/ResponsiveMessage.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ResponsiveMessage = () => {
4 | return (
5 |
6 |
7 |
This page looks better on Desktop
8 |
Please view on a larger screen for the best experience.
9 |
10 |
11 | );
12 | };
13 |
14 | export default ResponsiveMessage;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/ScrollToTop.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useLocation } from "react-router-dom";
3 |
4 | function ScrollToTop() {
5 | const { pathname } = useLocation();
6 |
7 | useEffect(() => {
8 | window.scrollTo(0, 0);
9 | }, [pathname]);
10 |
11 | return null;
12 | }
13 |
14 | export default ScrollToTop;
15 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/SettingsAttribute.jsx:
--------------------------------------------------------------------------------
1 | import SmallButton from "./SmallButton";
2 | import { Audio } from 'react-loader-spinner'
3 | const SettingsAttribute = ({ type, displayName, value, linkAction, loading, buttonName="Link", showButton=true, textArea=false, onChangeFunction }) => {
4 | return
5 | {displayName}:
6 | {textArea ? : {
18 | onChangeFunction(e.target.value);
19 | }}
20 | >}
21 | {showButton && : buttonName}
31 | action={linkAction}
32 | />}
33 |
34 | }
35 |
36 | export default SettingsAttribute;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/SkeletonLoader.jsx:
--------------------------------------------------------------------------------
1 | const SkeletonLoader = () => {
2 | return
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | };
20 |
21 | export default SkeletonLoader
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/SmallButton.jsx:
--------------------------------------------------------------------------------
1 | const SmallButton = ({ name, action, color, width = "5rem" }) => {
2 | const handleClick = () => {
3 | console.log("button clicked");
4 | }
5 | return
10 | {name}
11 |
12 | }
13 |
14 | export default SmallButton;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/Working.jsx:
--------------------------------------------------------------------------------
1 | const Working = () => {
2 | return
3 |
How does this work?
4 |
5 |
6 |
7 | Connect Services:
8 | Link your Gmail and Slack accounts to GmailGenius for seamless integration and automated email management.
9 |
10 |
11 | Configure Keywords:
12 | Add keywords along with corresponding email addresses and Slack channel names to customize your email routing and notification preferences.
13 |
14 |
15 | Automatic Processing:
16 | When a new email arrives in your inbox, GmailGenius automatically processes it using your predefined rules and settings.
17 |
18 |
19 | Intelligent Routing:
20 | Based on the configured keywords, GmailGenius forwards the email to the right email address ensuring efficient distribution of information.
21 |
22 |
23 | Slack Notification:
24 | GmailGenius also sends a notification to the corresponding Slack channel keeping your team informed in real-time.
25 |
26 |
27 |
28 |
29 | }
30 |
31 | export default Working;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/components/WorkingFlow.jsx:
--------------------------------------------------------------------------------
1 | import { FileSpreadsheet, ArrowRight } from "lucide-react"
2 | import Logo from "../assets/brain.svg";
3 | import NewMail from "./NewMail";
4 | import RedirectedTo from "./RedirectedTo";
5 |
6 | const WorkingFlow = () => {
7 | return (
8 |
9 |
10 | {/* Arrow and Brain Logo */}
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 | )
25 | }
26 |
27 | export default WorkingFlow;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 | import ResponsiveMessage from './components/ResponsiveMessage.jsx'
6 |
7 | createRoot(document.getElementById('root')).render(
8 |
9 |
10 |
11 | ,
12 | )
13 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/pages/Agent.jsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import AddAgent from "../components/AddAgent";
3 | import { useEffect, useState } from "react";
4 |
5 | const Agent = ({ user }) => {
6 | const navigate = useNavigate();
7 | const [isLoading, setIsLoading] = useState(true);
8 |
9 | return (
10 |
11 |
12 |
Enter Keywords (Crisp & Concise)
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | export default Agent;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import Hero from "../components/Hero";
2 | import Benefits from "../components/Benefits";
3 | import FAQ from "../components/FAQ";
4 | import Working from "../components/Working";
5 | import ActionButton from "../components/ActionButton";
6 | const Home = () => {
7 | return
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 | }
19 |
20 | export default Home;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import { signUpWithGoogle } from "../utils/authUtils";
3 |
4 | const Login = () => {
5 | const navigate = useNavigate();
6 |
7 | const handleGoogleSignUp = () => {
8 | signUpWithGoogle(navigate);
9 | };
10 |
11 | return (
12 |
13 |
14 |
You're logged out, login below
15 |
18 |
19 |
20 |
23 |
26 |
29 |
32 |
33 |
34 |
35 | Login In with Google
36 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | export default Login;
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/pages/NotFound.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | const NotFound = () => {
5 | return (
6 |
7 |
8 |
404
9 |
Something's missing.
10 |
Sorry, we can't find that page.
11 |
Back to Homepage
12 |
13 |
14 | );
15 | };
16 |
17 | export default NotFound;
18 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/src/utils/authUtils.js:
--------------------------------------------------------------------------------
1 | import { signInWithPopup, GoogleAuthProvider } from "firebase/auth";
2 | import { auth } from "../config/firebase";
3 | import { addUserData } from "../config/firebase";
4 |
5 | export const signUpWithGoogle = async (navigate) => {
6 | const provider = new GoogleAuthProvider();
7 | try {
8 | const result = await signInWithPopup(auth, provider);
9 | const user = result.user;
10 | await addUserData(user.uid, user.email.split("@")[0], user.email);
11 | navigate("/settings");
12 | } catch (error) {
13 | alert(error);
14 | console.error("Error during Google sign-up:", error);
15 | }
16 | };
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | darkMode: 'class',
4 | theme: {
5 | extend: {
6 | colors: {
7 | primary: {"50":"#faf5ff","100":"#f3e8ff","200":"#e9d5ff","300":"#d8b4fe","400":"#c084fc","500":"#a855f7","600":"#9333ea","700":"#7e22ce","800":"#6b21a8","900":"#581c87","950":"#3b0764"}
8 | },
9 | keyframes: {
10 | glow: {
11 | '0%, 100%': { boxShadow: '0 0 1px #fde68a, 0 0 4px #fde68a' },
12 | '50%': { boxShadow: '0 0 8px #fde68a, 0 0 8px #fde68a' },
13 | }
14 | },
15 | animation: {
16 | glow: 'glow 1s ease-in-out infinite',
17 | }
18 | },
19 | fontFamily: {
20 | 'body': [
21 | 'Inter',
22 | 'ui-sans-serif',
23 | 'system-ui',
24 | '-apple-system',
25 | 'system-ui',
26 | 'Segoe UI',
27 | 'Roboto',
28 | 'Helvetica Neue',
29 | 'Arial',
30 | 'Noto Sans',
31 | 'sans-serif',
32 | 'Apple Color Emoji',
33 | 'Segoe UI Emoji',
34 | 'Segoe UI Symbol',
35 | 'Noto Color Emoji'
36 | ],
37 | 'sans': [
38 | 'Inter',
39 | 'ui-sans-serif',
40 | 'system-ui',
41 | '-apple-system',
42 | 'system-ui',
43 | 'Segoe UI',
44 | 'Roboto',
45 | 'Helvetica Neue',
46 | 'Arial',
47 | 'Noto Sans',
48 | 'sans-serif',
49 | 'Apple Color Emoji',
50 | 'Segoe UI Emoji',
51 | 'Segoe UI Symbol',
52 | 'Noto Color Emoji'
53 | ]
54 | }
55 | },
56 | content: [
57 | "./index.html",
58 | "./src/**/*.{js,ts,jsx,tsx}",
59 | "./node_modules/flowbite-react/lib/esm/**/*.js",
60 | "./node_modules/flowbite/**/*.js"
61 | ],
62 | plugins: [
63 | require('flowbite/plugin')
64 | ],
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | {"source": "/(.*)", "destination": "/"}
4 | ]
5 | }
--------------------------------------------------------------------------------
/gmail-assistant/gmail-assistant-support/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/imgs/banner.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ComposioHQ/cookbook/af3a242220f6435c957bd8542ab7bf7e2ec89ab2/imgs/banner.gif
--------------------------------------------------------------------------------
/imgs/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ComposioHQ/cookbook/af3a242220f6435c957bd8542ab7bf7e2ec89ab2/imgs/banner.png
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/.env.example:
--------------------------------------------------------------------------------
1 | VITE_FIREBASE_API_KEY=
2 | VITE_FIREBASE_AUTH_DOMAIN=
3 | VITE_FIREBASE_PROJECT_ID=
4 | VITE_FIREBASE_STORAGE_BUCKET=
5 | VITE_FIREBASE_MESSAGING_SENDER_ID=
6 | VITE_FIREBASE_APP_ID=
7 | VITE_FIREBASE_MEASUREMENT_ID=
8 | VITE_BACKEND_URL=
9 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | .env
11 | backend/firebase/fb-auth-e06b5-0749e2fcbb63.json
12 | backend/attachments
13 | backend/firebase/__pycache__
14 |
15 | backend/__pycache__
16 |
17 |
18 | node_modules
19 | dist
20 | dist-ssr
21 | *.local
22 |
23 | # Editor directories and files
24 | .vscode/*
25 | !.vscode/extensions.json
26 | .idea
27 | .DS_Store
28 | *.suo
29 | *.ntvs*
30 | *.njsproj
31 | *.sln
32 | *.sw?
33 |
34 | backend/.env
35 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/.env.example:
--------------------------------------------------------------------------------
1 | OPENAI_API_KEY=
2 | MODEL=
3 | COMPOSIO_API_KEY=
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/agent_repost.py:
--------------------------------------------------------------------------------
1 | import os
2 | from composio_crewai import Action, ComposioToolSet
3 | from crewai import Agent, Crew, Task, Process
4 | from langchain_openai import ChatOpenAI
5 | from dotenv import load_dotenv
6 | from pathlib import Path
7 |
8 | load_dotenv()
9 |
10 | llm = ChatOpenAI(model="gpt-4o")
11 |
12 | # ComposioToolSet instance
13 | composio_toolset = ComposioToolSet(api_key=os.environ.get("COMPOSIO_API_KEY"))
14 |
15 | def repost_tweet_with_quote(quote: str, tweet_id: str) -> str:
16 | print("Reposting tweet with quote")
17 |
18 | # Tools
19 | tools = composio_toolset.get_actions(actions=[Action.TWITTER_USER_LOOKUP_BY_USERNAME])
20 |
21 | # Agent
22 | twitter_agent = Agent(
23 | role="Twitter Agent",
24 | goal="Repost tweets on Twitter with a quote",
25 | backstory="You're an AI assistant that reposts tweets with a quote on Twitter.",
26 | verbose=True,
27 | llm=llm,
28 | tools=tools,
29 | allow_delegation=False,
30 | )
31 |
32 | task_description = f"""
33 | 1. Repost the tweet with ID {tweet_id} with the following quote:
34 | "{quote}"
35 | """
36 |
37 | process_twitter_request = Task(
38 | description=task_description,
39 | agent=twitter_agent,
40 | expected_output="Confirmation that the tweet was reposted on Twitter with the quote.",
41 | )
42 |
43 | twitter_processing_crew = Crew(
44 | agents=[twitter_agent],
45 | tasks=[process_twitter_request],
46 | verbose=1,
47 | process=Process.sequential,
48 | )
49 |
50 | result = twitter_processing_crew.kickoff()
51 | return result
52 |
53 | repost_tweet_with_quote("This sounds cool", "1833201520021803153")
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/agent_tweet.py:
--------------------------------------------------------------------------------
1 | import os
2 | from composio_crewai import Action, ComposioToolSet
3 | from crewai import Agent, Crew, Task, Process
4 | from langchain_openai import ChatOpenAI
5 | from dotenv import load_dotenv
6 | from pathlib import Path
7 |
8 | load_dotenv()
9 |
10 | llm = ChatOpenAI(model="gpt-4o")
11 |
12 | # ComposioToolSet instance
13 | composio_toolset = ComposioToolSet(api_key=os.environ.get("COMPOSIO_API_KEY"))
14 |
15 | def post_twitter_message(message: str) -> str:
16 | print("Posting Twitter message")
17 |
18 | # Tools
19 | tools = composio_toolset.get_actions(actions=[Action.TWITTER_CREATION_OF_A_POST])
20 |
21 | # Agent
22 | twitter_agent = Agent(
23 | role="Twitter Agent",
24 | goal="Create and post tweets on Twitter",
25 | backstory="You're an AI assistant that crafts and shares tweets on Twitter.",
26 | verbose=True,
27 | llm=llm,
28 | tools=tools,
29 | allow_delegation=False,
30 | )
31 |
32 | task_description = f"""
33 | 1. Post the following message on Twitter:
34 | "{message}"
35 | """
36 |
37 | process_twitter_request = Task(
38 | description=task_description,
39 | agent=twitter_agent,
40 | expected_output="Confirmation that the tweet was posted on Twitter.",
41 | )
42 |
43 | twitter_processing_crew = Crew(
44 | agents=[twitter_agent],
45 | tasks=[process_twitter_request],
46 | verbose=1,
47 | process=Process.sequential,
48 | )
49 |
50 | result = twitter_processing_crew.kickoff()
51 | return result
52 |
53 | post_twitter_message("We will win this")
54 |
55 | # 1833201520021803153
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/composio_config.py:
--------------------------------------------------------------------------------
1 | from composio import ComposioToolSet, App, Composio, Action
2 | from composio.client.exceptions import NoItemsFound
3 | import os
4 | from dotenv import load_dotenv
5 | load_dotenv()
6 | from firebase.init import update_twitter_integration_id, get_composio_api_key, get_twitter_integration_id
7 |
8 | def isEntityConnected(ent_id: str, appType: str):
9 | toolset = ComposioToolSet(api_key=get_composio_api_key(ent_id),
10 | entity_id=ent_id)
11 | entity = toolset.get_entity()
12 | app_enum = getattr(App, appType)
13 | try:
14 | entity.get_connection(app=app_enum)
15 | response = {
16 | "authenticated": "yes",
17 | "message": f"User {ent_id} is authenticated with {appType}",
18 | }
19 | return response
20 | except NoItemsFound as e:
21 | response = {
22 | "authenticated": "no",
23 | "message": f"User {ent_id} is not authenticated with {appType}",
24 | }
25 | return response
26 |
27 | def createTwitterIntegrationAndInitiateAdminConnection(ent_id: str, redirectUrl: str):
28 | toolset = ComposioToolSet(api_key=get_composio_api_key(ent_id),
29 | entity_id=ent_id)
30 | entity = toolset.get_entity()
31 | twitter_app_id = "b3a9602b-731b-4044-9c9a-d0137ef5c887"
32 | integration = entity.client.integrations.create(name="Tweetify_integration", app_id=twitter_app_id, auth_mode="oauth2", use_composio_auth=True)
33 | update_twitter_integration_id(ent_id, integration.id)
34 | request = entity.initiate_connection("TWITTER", redirect_url=redirectUrl, integration=integration)
35 | response = {
36 | "authenticated": "no",
37 | "message": f"User {ent_id} is not yet authenticated with Twitter. Please authenticate.",
38 | "url": request.redirectUrl
39 | }
40 | return response
41 |
42 |
43 | def createNewEntity(ent_id: str, newUserId: str, redirectUrl: str):
44 | toolset = ComposioToolSet(api_key=get_composio_api_key(ent_id),
45 | entity_id=newUserId)
46 | entity = toolset.get_entity()
47 | try:
48 | entity.get_connection(app="TWITTER")
49 | response = {
50 | "authenticated": "yes",
51 | "message":
52 | f"User {newUserId} is already authenticated with TWITTER",
53 | "url": ""
54 | }
55 | return response
56 |
57 | except NoItemsFound as e:
58 | integration = entity.client.integrations.get_by_id(get_twitter_integration_id(ent_id))
59 | request = entity.initiate_connection(
60 | app_name="TWITTER", redirect_url=redirectUrl, integration=integration
61 | )
62 | response = {
63 | "authenticated": "no",
64 | "message": f"User {newUserId} is not yet authenticated with TWITTER. Please authenticate.",
65 | "url": request.redirectUrl
66 | }
67 | return response
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/firebase/init.py:
--------------------------------------------------------------------------------
1 | import firebase_admin
2 | from firebase_admin import credentials, auth, firestore
3 | from pathlib import Path
4 | import os
5 | from dotenv import load_dotenv
6 | load_dotenv()
7 |
8 | creds = {
9 | "type": os.environ.get("type"),
10 | "project_id": os.environ.get("project_id"),
11 | "private_key_id": os.environ.get("private_key_id"),
12 | "private_key": os.environ.get("private_key"),
13 | "client_email": os.environ.get("client_email"),
14 | "client_id": os.environ.get("client_id"),
15 | "auth_uri": os.environ.get("auth_uri"),
16 | "token_uri": os.environ.get("token_uri"),
17 | "auth_provider_x509_cert_url":
18 | os.environ.get("auth_provider_x509_cert_url"),
19 | "client_x509_cert_url": os.environ.get("client_x509_cert_url"),
20 | }
21 |
22 | firebase_admin.initialize_app(credentials.Certificate(creds))
23 | db = firestore.client()
24 |
25 | def get_user_by_username(username):
26 | users_ref = db.collection('users')
27 | query = users_ref.where('uid', '==', username).limit(1)
28 | docs = query.get()
29 |
30 | for doc in docs:
31 | return doc.to_dict()
32 |
33 | return False
34 |
35 | def update_twitter_integration_id(username: str, twitter_integration_id: str):
36 | users_ref = db.collection('users')
37 | query = users_ref.where('username', '==', username).limit(1)
38 | docs = query.get()
39 |
40 | for doc in docs:
41 | try:
42 | doc.reference.update(
43 | {'twitterIntegrationId': twitter_integration_id})
44 | print(f"Successfully updated twitterIntegrationId for user {username}")
45 | return True
46 | except Exception as e:
47 | print(f"Error updating twitterIntegrationId for user {username}: {e}")
48 | return False
49 |
50 | print(f"User {username} not found")
51 | return False
52 |
53 | def get_twitter_integration_id(username: str) -> str:
54 | users_ref = db.collection('users')
55 | query = users_ref.where('username', '==', username).limit(1)
56 | docs = query.get()
57 |
58 | for doc in docs:
59 | user_data = doc.to_dict()
60 | return user_data.get('twitterIntegrationId', '')
61 |
62 | print(f"User {username} not found")
63 | return ''
64 |
65 |
66 | def get_composio_api_key(username: str) -> str:
67 | users_ref = db.collection('users')
68 | query = users_ref.where('username', '==', username).limit(1)
69 | docs = query.get()
70 |
71 | for doc in docs:
72 | user_data = doc.to_dict()
73 | return user_data.get('composio_api_key', '')
74 |
75 | print(f"User {username} not found")
76 | return ''
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/get_connections.py:
--------------------------------------------------------------------------------
1 | from composio import Composio
2 | composio_client = Composio()
3 | # res = composio_client.connected_accounts.get("3d87fb0d...")
4 | print(f"Status: {res.status}")
5 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/initialise_agent.py:
--------------------------------------------------------------------------------
1 | import json
2 | from composio_crewai import Action, ComposioToolSet
3 | from crewai import Agent, Crew, Task, Process
4 | from langchain_openai import ChatOpenAI
5 | from dotenv import load_dotenv
6 | from crewai_tools.tools.base_tool import BaseTool
7 | from firebase.init import db
8 | import os
9 |
10 | load_dotenv()
11 | llm = ChatOpenAI(model="gpt-4o")
12 |
13 |
14 | def initialise(entityId: str):
15 | username = entityId
16 |
17 | def get_user_by_username(username):
18 | users_ref = db.collection('users')
19 | query = users_ref.where('username', '==', username).limit(1)
20 | docs = query.get()
21 |
22 | for doc in docs:
23 | return doc.to_dict()
24 |
25 | return False
26 |
27 | user = get_user_by_username(entityId)
28 | keywords = user['keywords'][0]['keywords']
29 | email = user['email']
30 | composio_toolset = ComposioToolSet(
31 | api_key=os.environ.get("COMPOSIO_API_KEY"), entity_id=entityId)
32 |
33 | google_tools = composio_toolset.get_actions(actions=[Action.GMAIL_SEND_EMAIL])
34 |
35 | google_assistant = Agent(
36 | role="Gmail Assistant",
37 | goal= """
38 | 1. Send an email to the user with the specified keywords in the body.
39 | """,
40 | backstory=
41 | "You're an AI assistant that handles Gmail operations using Gmail APIs.",
42 | verbose=True,
43 | llm=llm,
44 | tools=google_tools,
45 | allow_delegation=False,
46 | )
47 |
48 | send_email_task = Task(
49 | description=f"""
50 | Send an email with the following details:
51 | 1. Recipient email: {email}
52 | 2. Subject: "Test email with keywords"
53 | 3. Body: "This is a test email containing the following keywords: {keywords}. These keywords will be used to filter and forward your emails."
54 | Only specify recipient_email, subject, and body when sending the email.
55 | """,
56 | agent=google_assistant,
57 | expected_output=
58 | "Send an email to the user with the specified keywords",
59 | )
60 |
61 | gmail_processing_crew = Crew(
62 | agents=[google_assistant],
63 | tasks=[send_email_task],
64 | verbose=1,
65 | process=Process.sequential,
66 | )
67 | return gmail_processing_crew.kickoff()
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/prompt.py:
--------------------------------------------------------------------------------
1 | prompt1 = f"""
2 | 1. Send an automatic reply to the sender of the email with the following message:
3 | "Thank you for your email. We have received it and will get back to you shortly"
4 | using GMAIL_REPLY_TO_THREAD action & if any attachments are present, use GMAIL_SEND_EMAIL send that too.
5 | 2. Check if the email subject (subject) or body (messageText) in the payload contains any of the keywords specified in this dictionary: {[{'slackChannel': 'dev-channel', 'email': 'hrishikeshvastrad14@gmail.com', 'keywords': 'bugs, errors, issues'}, {'slackChannel': 'growth-channel', 'email': 'mskarthikugalawat@gmail.com', 'keywords': 'Collaboration, partnership, sponser'}, {'slackChannel': 'hrishikesh-channel', 'email': 'hrishikesh@gmail.com', 'keywords': 'bill'}]}.
6 | 3. If a keyword match is found:
7 | a. Check if the original email contains any attachments.
8 | b. If attachments are present, use the GMAIL_GET_ATTACHMENT action to download them.
9 | c. Send the email payload to the corresponding email address and slack channel. And if attachments are present include the downloaded attachments.
10 | message: 'Forwarded email: subject & body'
11 | Payload: {payload}
12 | """
13 | prompt2 = f"""
14 | 1. Check if the email subject (subject) or body (messageText) in the payload contains any of the keywords specified in this dictionary: {keywords}.
15 | 2. If a keyword match is found:
16 | a. Check if the original email contains any attachments.
17 | b. If attachments are present, use the GMAIL_GET_ATTACHMENT action to download them.
18 | c. Send the email payload to the corresponding email address and slack channel. And if attachments are present include the downloaded attachments.
19 | message: 'Forwarded email: subject & body'
20 | Payload: {payload}
21 | """
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/quote_generator.py:
--------------------------------------------------------------------------------
1 | import os
2 | from openai import OpenAI
3 | from dotenv import load_dotenv
4 |
5 | load_dotenv()
6 |
7 | client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
8 |
9 | def generate_repost_quote(tweet_content: str, number_of_quotes: int) -> list[str]:
10 | quotes = []
11 |
12 | for _ in range(number_of_quotes):
13 | completion = client.chat.completions.create(
14 | model="gpt-4o-mini",
15 | messages=[
16 | {"role": "system", "content": "Generate a meaningful repost quote with hashtags and an emoji"},
17 | {"role": "user", "content": f"Generate a repost quote for this tweet: '{tweet_content}'. Include hashtags and an emoji in the same string. Please keep it short, simple, informal and no filler words, also sound like a human."}
18 | ]
19 | )
20 |
21 | quote = completion.choices[0].message.content.strip()
22 | quotes.append(quote)
23 |
24 | return quotes
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/repost_existing_tweet.py:
--------------------------------------------------------------------------------
1 | import os
2 | from composio_crewai import Action, ComposioToolSet
3 | from crewai import Agent, Crew, Task, Process
4 | from langchain_openai import ChatOpenAI
5 | from dotenv import load_dotenv
6 | from pathlib import Path
7 | from firebase.init import get_composio_api_key
8 | from twitter_functions import get_user_id_by_username
9 |
10 | load_dotenv()
11 |
12 | llm = ChatOpenAI(model="gpt-4o")
13 |
14 | def repost_tweet(entity_id: str, task_description: str) -> str:
15 | composio_toolset = ComposioToolSet(api_key=get_composio_api_key(entity_id), entity_id=entity_id)
16 | # tools = composio_toolset.get_actions(actions=[Action.TWITTER_CREATION_OF_A_POST, Action.TWITTER_CAUSES_THE_USER_IN_THE_PATH_TO_REPOST_THE_SPECIFIED_POST, Action.TWITTER_USER_LOOKUP_BY_USERNAME])
17 | tools = composio_toolset.get_actions(actions=[Action.TWITTER_CREATION_OF_A_POST, Action.TWITTER_CAUSES_THE_USER_IN_THE_PATH_TO_REPOST_THE_SPECIFIED_POST])
18 | twitter_agent = Agent(
19 | role="Twitter Agent",
20 | goal="Repost tweets on Twitter",
21 | backstory="You're an AI assistant that reposts tweets on Twitter, if a quote is provided, add the quote to the tweet, if no quote is provided, repost the tweet without a quote.",
22 | verbose=True,
23 | llm=llm,
24 | tools=tools,
25 | allow_delegation=False,
26 | )
27 |
28 | process_twitter_request = Task(
29 | description=task_description,
30 | agent=twitter_agent,
31 | expected_output="Result of the repost",
32 | )
33 |
34 | twitter_processing_crew = Crew(
35 | agents=[twitter_agent],
36 | tasks=[process_twitter_request],
37 | verbose=1,
38 | process=Process.sequential,
39 | )
40 |
41 | result = twitter_processing_crew.kickoff()
42 | return result
43 |
44 |
45 | def repost_existing(tweet_id: str, repost_data_list: list):
46 | for repost_data in repost_data_list:
47 | entity_id = repost_data["entity_id"]
48 | quote = repost_data["quote"]
49 |
50 | if quote:
51 | task_description = f"""
52 | Repost the tweet with ID {tweet_id} with the following quote:
53 | "{quote}"
54 | """
55 | else:
56 | user_id = get_user_id_by_username(entity_id)
57 | task_description = f"""
58 | Repost the tweet with ID {tweet_id} without any quote and user ID {user_id}
59 | """
60 |
61 | repost_result = repost_tweet(entity_id, task_description)
62 | print(f"Repost result for {entity_id}: {repost_result}")
63 |
64 | return "Tweeting and reposting process completed."
65 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/requirements.txt:
--------------------------------------------------------------------------------
1 | crewai
2 | composio-crewai
3 | langchain-openai
4 | python-dotenv
5 | crewai_tools
6 | fastapi
7 | uvicorn
8 | langchain_google_genai
9 | firebase-admin
10 |
11 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/setup.sh:
--------------------------------------------------------------------------------
1 |
2 | #!/bin/bash
3 |
4 | # Create a virtual environment
5 | echo "Creating virtual environment..."
6 | python3 -m venv ~/.venvs/gmail_agent
7 |
8 | # Activate the virtual environment
9 | echo "Activating virtual environment..."
10 | source ~/.venvs/gmail_agent/bin/activate
11 |
12 | # Install libraries from requirements.txt
13 | echo "Installing libraries from requirements.txt..."
14 | pip install -r requirements.txt
15 |
16 | # Login to your account
17 | echo "Login to your Composio acount"
18 | composio login
19 |
20 | # Add calendar tool
21 | echo "Add Gmail tools. Finish the flow"
22 | composio add gmail
23 | composio add googlesheets
24 |
25 | # Copy env backup to .env file
26 | if [ -f ".env.example" ]; then
27 | echo "Copying .env.example to .env..."
28 | cp .env.example .env
29 | else
30 | echo "No .env.example file found. Creating a new .env file..."
31 | touch .env
32 | fi
33 |
34 | # Prompt user to fill the .env file
35 | echo "Please fill in the .env file with the necessary environment variables."
36 |
37 | echo "Setup completed successfully!"
38 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/backend/twitter_functions.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import os
3 | from dotenv import load_dotenv
4 | load_dotenv()
5 |
6 | def get_tweet_text_by_id(tweet_id):
7 | url = f"https://api.twitter.com/2/tweets/{tweet_id}"
8 | bearer_token = os.getenv("TWITTER_TOKEN")
9 | headers = {
10 | "Authorization": f"Bearer {bearer_token}"
11 | }
12 | try:
13 | response = requests.get(url, headers=headers)
14 | response.raise_for_status()
15 | return response.json()['data']['text'].replace('\n', '')
16 | except requests.exceptions.RequestException as e:
17 | print(f"An error occurred: {e}")
18 | return None
19 |
20 | def get_user_id_by_username(username):
21 | url = f"https://api.x.com/2/users/by/username/{username}"
22 | bearer_token = os.getenv("TWITTER_TOKEN")
23 | headers = {
24 | "Authorization": f"Bearer {bearer_token}"
25 | }
26 | try:
27 | response = requests.get(url, headers=headers)
28 | response.raise_for_status()
29 | return response.json()['data']['id']
30 | except requests.exceptions.RequestException as e:
31 | print(f"An error occurred: {e}")
32 | return None
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import react from 'eslint-plugin-react'
4 | import reactHooks from 'eslint-plugin-react-hooks'
5 | import reactRefresh from 'eslint-plugin-react-refresh'
6 |
7 | export default [
8 | { ignores: ['dist'] },
9 | {
10 | files: ['**/*.{js,jsx}'],
11 | languageOptions: {
12 | ecmaVersion: 2020,
13 | globals: globals.browser,
14 | parserOptions: {
15 | ecmaVersion: 'latest',
16 | ecmaFeatures: { jsx: true },
17 | sourceType: 'module',
18 | },
19 | },
20 | settings: { react: { version: '18.3' } },
21 | plugins: {
22 | react,
23 | 'react-hooks': reactHooks,
24 | 'react-refresh': reactRefresh,
25 | },
26 | rules: {
27 | ...js.configs.recommended.rules,
28 | ...react.configs.recommended.rules,
29 | ...react.configs['jsx-runtime'].rules,
30 | ...reactHooks.configs.recommended.rules,
31 | 'react/jsx-no-target-blank': 'off',
32 | 'react-refresh/only-export-components': [
33 | 'warn',
34 | { allowConstantExport: true },
35 | ],
36 | },
37 | },
38 | ]
39 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | GmailGenius
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gmailgenius",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@headlessui/react": "^2.1.8",
14 | "@heroicons/react": "^2.1.5",
15 | "axios": "^1.7.5",
16 | "composio-core": "^0.1.17-rc.2",
17 | "firebase": "^10.13.0",
18 | "flowbite": "^2.5.1",
19 | "flowbite-react": "^0.10.1",
20 | "lucide-react": "^0.436.0",
21 | "notistack": "^3.0.1",
22 | "react": "^18.3.1",
23 | "react-dom": "^18.3.1",
24 | "react-icons": "^5.3.0",
25 | "react-loader-spinner": "^6.1.6",
26 | "rsuite": "^5.70.3"
27 | },
28 | "devDependencies": {
29 | "@eslint/js": "^9.9.0",
30 | "@types/react": "^18.3.3",
31 | "@types/react-dom": "^18.3.0",
32 | "@vitejs/plugin-react": "^4.3.1",
33 | "autoprefixer": "^10.4.20",
34 | "eslint": "^9.9.0",
35 | "eslint-plugin-react": "^7.35.0",
36 | "eslint-plugin-react-hooks": "^5.1.0-rc.0",
37 | "eslint-plugin-react-refresh": "^0.4.9",
38 | "globals": "^15.9.0",
39 | "postcss": "^8.4.42",
40 | "react-router-dom": "^6.26.1",
41 | "tailwindcss": "^3.4.10",
42 | "vite": "^5.4.1"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
2 | import { onAuthStateChanged } from "firebase/auth";
3 | import { auth } from "./config/firebase";
4 | import Navbar from "./components/Navbar";
5 | import Home from "./pages/Home";
6 | import Footer from "./components/Footer";
7 | import ScrollToTop from "./components/ScrollToTop";
8 | import { useState, useEffect } from "react";
9 | import Login from "./pages/Login";
10 | import Settings from "./pages/Settings";
11 | import NotFound from "./pages/NotFound";
12 | import SkeletonLoader from "./components/SkeletonLoader";
13 | import { SnackbarProvider } from 'notistack'
14 | import CreatePost from "./pages/CreatePost";
15 | import Repost from "./pages/Repost";
16 |
17 | const ProtectedRoute = ({ user, children }) => {
18 | if (!user) {
19 | return ;
20 | }
21 | return children;
22 | };
23 |
24 | const App = () => {
25 | const [user, setUser] = useState(null);
26 | const [loading, setLoading] = useState(true);
27 |
28 | useEffect(() => {
29 | const unsubscribe = onAuthStateChanged(auth, (user) => {
30 | setUser(user);
31 | setLoading(false);
32 | });
33 |
34 | return () => unsubscribe();
35 | }, []);
36 |
37 | if (loading) {
38 | return
39 | }
40 |
41 | return (
42 |
43 |
44 |
45 |
46 |
47 | } />
48 |
50 |
51 |
52 | } />
53 |
55 |
56 |
57 | } />
58 |
60 |
61 |
62 | } />
63 | } />
64 | } />
65 |
66 |
67 |
68 |
69 | );
70 | }
71 |
72 | export default App;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/assets/gmailLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ComposioHQ/cookbook/af3a242220f6435c957bd8542ab7bf7e2ec89ab2/twitter-assistant/twitter-retweet-helper/src/assets/gmailLogo.png
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/assets/slackLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ComposioHQ/cookbook/af3a242220f6435c957bd8542ab7bf7e2ec89ab2/twitter-assistant/twitter-retweet-helper/src/assets/slackLogo.png
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/assets/twitterUI/main_post.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ComposioHQ/cookbook/af3a242220f6435c957bd8542ab7bf7e2ec89ab2/twitter-assistant/twitter-retweet-helper/src/assets/twitterUI/main_post.png
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/assets/twitterUI/repost1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ComposioHQ/cookbook/af3a242220f6435c957bd8542ab7bf7e2ec89ab2/twitter-assistant/twitter-retweet-helper/src/assets/twitterUI/repost1.png
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/assets/twitterUI/repost2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ComposioHQ/cookbook/af3a242220f6435c957bd8542ab7bf7e2ec89ab2/twitter-assistant/twitter-retweet-helper/src/assets/twitterUI/repost2.png
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/ActionButton.jsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import { signUpWithGoogle } from "../utils/firebase_utils";
3 | import { auth } from "../config/firebase";
4 | import { useEffect, useState } from "react";
5 |
6 | const ActionButton = ({ displayName }) => {
7 | const navigate = useNavigate();
8 | const [user, setUser] = useState(null);
9 |
10 | useEffect(() => {
11 | const unsubscribe = auth.onAuthStateChanged((user) => {
12 | setUser(user);
13 | });
14 |
15 | return () => unsubscribe();
16 | }, []);
17 |
18 | const handleClick = async () => {
19 | if (user) {
20 | navigate("/settings");
21 | } else {
22 | try {
23 | await signUpWithGoogle(navigate);
24 | } catch (error) {
25 | console.error("Error during sign up:", error);
26 | }
27 | }
28 | };
29 |
30 | return (
31 |
35 | {displayName}
36 |
37 |
38 | );
39 | };
40 |
41 | export default ActionButton;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/AddNewUserOutline.jsx:
--------------------------------------------------------------------------------
1 | import 'rsuite/Loader/styles/index.css';
2 | import { Loader } from 'rsuite';
3 |
4 | export default function AddNewUserOutline({ loading, onClick }) {
5 | return (
6 |
11 | {loading ? : (
12 | <>
13 |
20 |
26 |
27 | Add a new user
28 | >
29 | )}
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/AddNewUserSearchBar.jsx:
--------------------------------------------------------------------------------
1 | import {
2 | Combobox,
3 | ComboboxInput,
4 | Dialog,
5 | DialogPanel,
6 | DialogBackdrop,
7 | } from '@headlessui/react'
8 | import { MagnifyingGlassIcon } from '@heroicons/react/20/solid'
9 |
10 | export default function AddNewUserSearchBar({ open, setOpen, newUser, setNewUser, handleNewAddUser }) {
11 | const handleKeyDown = (e) => {
12 | if (e.key === 'Enter') {
13 | handleNewAddUser(newUser)
14 | setOpen(false)
15 | setNewUser('')
16 | }
17 | }
18 |
19 | return (
20 | {
24 | setOpen(false)
25 | setNewUser('')
26 | }}
27 | >
28 |
32 |
33 |
34 |
38 |
39 |
40 |
44 | setNewUser(event.target.value)}
49 | value={newUser}
50 | onKeyDown={handleKeyDown}
51 | />
52 |
53 |
54 |
55 |
56 |
57 | )
58 | }
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/AddedUsers.jsx:
--------------------------------------------------------------------------------
1 | import TwitterUserCard from "./TwitterUserCard";
2 | import { checkConnectionStatus } from "../utils/composio_utils";
3 | import { useState, useEffect, useCallback } from "react";
4 | import SkeletonLoader from "./SkeletonLoader";
5 | import { updateAuthorisedUserConnectionStatus } from "../config/firebase";
6 |
7 | const AddedUsers = ({ authorisedUsers, adminId }) => {
8 | const [connectedUsers, setConnectedUsers] = useState([]);
9 | const [connected, setConnected] = useState(false);
10 | const [fetchingUsers, setFetchingUsers] = useState(false);
11 |
12 | const checkUsersConnection = useCallback(async () => {
13 | setFetchingUsers(true);
14 | const updatedUsers = await Promise.all(
15 | authorisedUsers.map(async (user) => {
16 | let isConnected = user.isConnected;
17 | if (!isConnected) {
18 | let res = await checkConnectionStatus("TWITTER", setConnected, user.username);
19 | if (res === "yes") {
20 | isConnected = true;
21 | await updateAuthorisedUserConnectionStatus(adminId, user.username);
22 | }
23 | }
24 | return { ...user, isConnected };
25 | })
26 | );
27 | setConnectedUsers(updatedUsers);
28 | setFetchingUsers(false);
29 | }, [authorisedUsers]);
30 |
31 | useEffect(() => {
32 | checkUsersConnection();
33 | }, [checkUsersConnection]);
34 |
35 | return (<>
36 | {
41 | const searchTerm = e.target.value.toLowerCase();
42 | setConnectedUsers(
43 | authorisedUsers.filter((user) =>
44 | user.username.toLowerCase().includes(searchTerm)
45 | )
46 | );
47 | }}
48 | />
49 |
50 | {fetchingUsers ? (
51 |
52 | ) : (
53 | connectedUsers.length > 0 ? (
54 | connectedUsers.map((user) => (
55 |
56 | ))
57 | ) : (
58 |
No authorised users added yet.
59 | )
60 | )}
61 |
62 | >
63 | );
64 | }
65 |
66 | export default AddedUsers;
67 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/Avatar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { Dropdown } from 'flowbite';
3 | import { logOut } from '../config/firebase';
4 | import { Link } from 'react-router-dom';
5 | const Avatar = ({ user }) => {
6 | useEffect(() => {
7 | if (user) {
8 | const $targetEl = document.getElementById('userDropdown');
9 | const $triggerEl = document.getElementById('avatarButton');
10 |
11 | if ($targetEl && $triggerEl) {
12 | new Dropdown($targetEl, $triggerEl, {
13 | placement: 'bottom-start',
14 | triggerType: 'click'
15 | });
16 | }
17 | }
18 | }, [user]);
19 |
20 | if (!user) return null;
21 |
22 | return (
23 |
24 |
32 |
33 |
34 |
{user.displayName}
35 |
{user.email}
36 |
37 |
38 |
39 | Home
40 |
41 |
42 | Create New Tweet & Repost
43 |
44 |
45 | Repost Existing Tweet
46 |
47 |
48 | Settings
49 |
50 |
51 |
54 |
55 |
56 | )
57 | }
58 |
59 | export default Avatar;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/BenefitCard.jsx:
--------------------------------------------------------------------------------
1 | const BenefitCard = ({ title, body }) => {
2 | return (
3 |
7 |
8 | {title}
9 |
10 |
11 | {body}
12 |
13 |
14 | );
15 | };
16 |
17 | export default BenefitCard;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/Benefits.jsx:
--------------------------------------------------------------------------------
1 | import BenefitCard from "./BenefitCard";
2 |
3 | const benefits = [
4 | {
5 | "title": "AI-Powered Tweet Creation",
6 | "body": "Tweetify uses advanced AI to help you create engaging tweets and posts tailored to your audience and brand voice"
7 | },
8 | {
9 | "title": "Smart Scheduling",
10 | "body": "Easily schedule your tweets for optimal posting times, ensuring maximum visibility and engagement for your content"
11 | },
12 | {
13 | "title": "Seamless Account Connections",
14 | "body": "Generate unique links to securely connect multiple Twitter accounts, allowing you to manage and post on behalf of others"
15 | },
16 | {
17 | "title": "Retweet Content Generation",
18 | "body": "Automatically generate relevant and engaging retweet content to boost your social media presence and increase interaction"
19 | }
20 | ]
21 |
22 | const Benefits = () => {
23 | return
24 |
Benefits
25 |
26 | {
27 | benefits.map((benefit, idx) =>
28 |
29 | )
30 | }
31 |
32 |
33 | }
34 |
35 | export default Benefits;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/ComingSoon.jsx:
--------------------------------------------------------------------------------
1 | import { Dialog, DialogBackdrop, DialogPanel, DialogTitle } from '@headlessui/react'
2 | // import { ExclamationIcon } from '@heroicons/react/24/outline'
3 |
4 | export default function ComingSoon({ open, setOpen }) {
5 | return (
6 | setOpen(false)} className="relative z-10">
7 |
11 |
12 |
13 |
14 |
18 |
19 | {/*
20 |
21 |
*/}
22 |
23 |
24 | Feature coming soon!
25 |
26 |
27 |
28 | We are working hard to bring this feature to you. Stay tuned for updates!
29 |
30 |
31 |
32 |
33 |
34 | setOpen(false)}
37 | className="w-24 mx-auto block focus:outline-none text-white bg-purple-700 hover:bg-purple-800 font-medium rounded-lg text-sm px-5 py-2.5 h-[2.5rem]"
38 | >
39 | Close
40 |
41 |
42 |
43 |
44 |
45 |
46 | )
47 | }
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/CreatePostTextArea.jsx:
--------------------------------------------------------------------------------
1 | import 'rsuite/Loader/styles/index.css';
2 | import { Loader } from 'rsuite';
3 | import ComingSoon from './ComingSoon';
4 | import { useState } from 'react';
5 | export default function CreatePostTextArea({ post, setPost, handlePost, handleGenerateQuotes, posting, generatingQuotes }) {
6 | const [comingSoon, setComingSoon] = useState(false);
7 | return (
8 |
51 | )
52 | }
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/Footer.jsx:
--------------------------------------------------------------------------------
1 | import LogoComponent from "./LogoComponent";
2 | const Footer = () => {
3 | return
4 |
5 |
40 |
41 |
42 |
© 2024 Tweetify | Made with ❤️ by Composio
43 |
44 |
45 |
46 |
47 | }
48 |
49 | export default Footer;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/GetPostTextArea.jsx:
--------------------------------------------------------------------------------
1 | import 'rsuite/Loader/styles/index.css';
2 | import { Loader } from 'rsuite';
3 |
4 | export default function GetPostTextArea({ post, setPost, handlePost, handleGenerateQuotes, posting, generatingQuotes }) {
5 | return (
6 |
7 |
8 |
handlePost(e)} className="relative">
9 |
23 |
24 |
25 | Advanced Options
26 |
27 |
28 |
33 | {generatingQuotes ? : "Generate Retweet Quotes"}
34 |
35 |
39 | {posting ? : "Repost"}
40 |
41 |
42 |
43 |
44 |
45 |
46 | )
47 | }
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/Hero.jsx:
--------------------------------------------------------------------------------
1 | import ActionButton from "./ActionButton";
2 | import WorkingFlow from "./WorkingFlow";
3 |
4 | const Hero = () => {
5 | return <>
6 | ⚡️Supercharge your Twitter
7 | Improve your Twitter game with Tweetify! Create and schedule posts, share permissions effortlessly, and let our AI generate engaging retweet content. Amplify your reach while you focus on what matters most.
8 |
9 |
12 |
13 | >
14 | }
15 |
16 | export default Hero;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/LogoComponent.jsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom"
2 | import Logo from "../assets/brain.svg";
3 | const LogoComponent = () => {
4 | return
5 |
6 | Tweetify
7 |
8 | }
9 |
10 | export default LogoComponent;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import Avatar from "./Avatar";
2 | import LogoComponent from "./LogoComponent";
3 | const Navbar = ({ user }) => {
4 | return
5 |
17 |
18 |
19 | }
20 |
21 | export default Navbar;
22 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/NewMail.jsx:
--------------------------------------------------------------------------------
1 | import { Mail, Paperclip } from "lucide-react"
2 | const NewMail = () => {
3 | return
4 |
5 |
6 |
Gmail
7 |
8 |
9 |
10 |
11 |
12 |
13 | Subject
14 |
15 |
16 | Message
17 |
18 |
19 |
20 |
21 |
22 | Bug Report: App Crashing on Launch
23 |
24 |
25 | Your app is constantly crashing when I try to open it. This is unacceptable! I've been a loyal user for years and now I can't even...
26 |
27 |
28 |
29 |
30 | {/* Hackathon Collaboration Invitation */}
31 | Hackathon Collaboration Invitation
32 |
33 |
34 | We're hosting a hackathon and would love to have you collaborate with us. Your tools would be...
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | }
43 |
44 | export default NewMail;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/RedirectedTo.jsx:
--------------------------------------------------------------------------------
1 | import gmailLogo from '../assets/gmailLogo.png'
2 | import slackLogo from '../assets/slackLogo.png'
3 | import repost1 from '../assets/twitterUI/repost1.png'
4 | import repost2 from '../assets/twitterUI/repost2.png'
5 |
6 | const RedirectedTo = () => {
7 | return
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | }
16 |
17 | export default RedirectedTo;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/ResponsiveMessage.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ResponsiveMessage = () => {
4 | return (
5 |
6 |
7 |
This page looks better on Desktop
8 |
Please view on a larger screen for the best experience.
9 |
10 |
11 | );
12 | };
13 |
14 | export default ResponsiveMessage;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/ScrollToTop.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useLocation } from "react-router-dom";
3 |
4 | function ScrollToTop() {
5 | const { pathname } = useLocation();
6 |
7 | useEffect(() => {
8 | window.scrollTo(0, 0);
9 | }, [pathname]);
10 |
11 | return null;
12 | }
13 |
14 | export default ScrollToTop;
15 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/Separator.jsx:
--------------------------------------------------------------------------------
1 | const Separator = ({ title }) => {
2 | return (
3 |
4 |
{title}
5 |
6 | );
7 | };
8 |
9 | export default Separator;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/SettingsAttribute.jsx:
--------------------------------------------------------------------------------
1 | import SmallButton from "./SmallButton";
2 | import { Audio } from 'react-loader-spinner'
3 | import 'rsuite/Loader/styles/index.css';
4 | import { Loader } from 'rsuite';
5 |
6 | const SettingsAttribute = ({ type, displayName, value, linkAction, loading, buttonName="Link", showButton=true, textArea=false, onChangeFunction, readOnly=true, nolabel=false, placeholder="" }) => {
7 | return
8 | {!nolabel && {displayName}: }
9 | {textArea ? {
14 | onChangeFunction(e.target.value);
15 | }}
16 | > : {
22 | onChangeFunction(e.target.value);
23 | }}
24 | autoComplete="off"
25 | >}
26 | {showButton && : buttonName}
28 | action={linkAction}
29 | />}
30 |
31 | }
32 |
33 | export default SettingsAttribute;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/SkeletonLoader.jsx:
--------------------------------------------------------------------------------
1 | const SkeletonLoader = () => {
2 | return
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | };
20 |
21 | export default SkeletonLoader
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/SmallButton.jsx:
--------------------------------------------------------------------------------
1 | const SmallButton = ({ name, action, color, width = "5rem", type="button" }) => {
2 | const handleClick = () => {
3 | console.log("button clicked");
4 | }
5 | return
10 | {name}
11 |
12 | }
13 |
14 | export default SmallButton;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/TwitterUserCard.jsx:
--------------------------------------------------------------------------------
1 | import { BsCheckCircle } from "react-icons/bs";
2 | import SmallButton from "./SmallButton";
3 | import { Audio } from 'react-loader-spinner';
4 | import { useState } from "react";
5 | import Clipboard from "./Clipboard";
6 |
7 | const TwitterUserCard = ({ user, action, connected, authUrl }) => {
8 | return
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
23 | {connected ?
24 |
25 |
26 |
27 | :
28 |
29 | }
30 |
31 |
32 | }
33 |
34 | export default TwitterUserCard;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/UsersIcon.jsx:
--------------------------------------------------------------------------------
1 | const UsersIcon = () => {
2 | return (
3 |
4 |
11 |
17 |
18 |
Added Users
19 |
20 | )
21 | }
22 |
23 | export default UsersIcon;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/Working.jsx:
--------------------------------------------------------------------------------
1 | const Working = () => {
2 | return
3 |
How does Tweetify work?
4 |
5 |
6 |
7 | Create a Post:
8 | Craft your tweet content using Tweetify's intuitive interface, allowing you to compose engaging messages for your audience.
9 |
10 |
11 | Schedule Your Tweet:
12 | Choose the perfect time for your tweet to go live by utilizing our scheduling feature , ensuring maximum engagement with your followers.
13 |
14 |
15 | Generate Sharing Links:
16 | Tweetify creates unique permission links that you can share with others, allowing them to connect their Twitter accounts and grant you posting privileges.
17 |
18 |
19 | Expand Your Reach:
20 | As users grant you permission, you can post on their behalf , significantly increasing your content's visibility and reach across multiple accounts.
21 |
22 |
23 | Generate Retweet Content:
24 | Tweetify helps you create engaging retweet content , making it easy to repurpose and amplify your original tweets for maximum impact.
25 |
26 |
27 |
28 |
29 | }
30 |
31 | export default Working;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/components/WorkingFlow.jsx:
--------------------------------------------------------------------------------
1 | import { FileSpreadsheet, ArrowRight } from "lucide-react"
2 | import Logo from "../assets/brain.svg";
3 | import NewMail from "./NewMail";
4 | import RedirectedTo from "./RedirectedTo";
5 | import main_post from "../assets/twitterUI/main_post.png";
6 | import repost1 from "../assets/twitterUI/repost1.png";
7 | import repost2 from "../assets/twitterUI/repost2.png";
8 |
9 | const WorkingFlow = () => {
10 | return (
11 |
12 |
13 | {/* Arrow and Brain Logo */}
14 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
27 | )
28 | }
29 |
30 | export default WorkingFlow;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 | import ResponsiveMessage from './components/ResponsiveMessage.jsx'
6 |
7 | createRoot(document.getElementById('root')).render(
8 |
9 |
10 |
11 | ,
12 | )
13 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import Hero from "../components/Hero";
2 | import Benefits from "../components/Benefits";
3 | import FAQ from "../components/FAQ";
4 | import Working from "../components/Working";
5 | import ActionButton from "../components/ActionButton";
6 | const Home = () => {
7 | return
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 | }
19 |
20 | export default Home;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from "react-router-dom";
2 | import { signUpWithGoogle } from "../utils/firebase_utils";
3 |
4 | const Login = () => {
5 | const navigate = useNavigate();
6 |
7 | const handleGoogleSignUp = () => {
8 | signUpWithGoogle(navigate);
9 | };
10 |
11 | return (
12 |
13 |
14 |
You're logged out, login below
15 |
18 |
19 |
20 |
23 |
26 |
29 |
32 |
33 |
34 |
35 | Login In with Google
36 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | export default Login;
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/pages/NotFound.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | const NotFound = () => {
5 | return (
6 |
7 |
8 |
404
9 |
Something's missing.
10 |
Sorry, we can't find that page.
11 |
Back to Homepage
12 |
13 |
14 | );
15 | };
16 |
17 | export default NotFound;
18 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/pages/Settings.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState, useCallback } from "react";
2 | import SettingsAttribute from "../components/SettingsAttribute";
3 | import { checkConnectionStatus, linkTwitterAccount, initiateTwitterConnectionAndConnectAdmin } from "../utils/composio_utils";
4 | import { getComposioApiKey, updateComposioApiKey } from "../config/firebase";
5 | import { useSnackbar } from 'notistack'
6 | import Separator from "../components/Separator";
7 | import AddNewUser from "../components/AddNewUser";
8 |
9 | const Settings = ({ user }) => {
10 | const { enqueueSnackbar } = useSnackbar();
11 | const [username, setUsername] = useState("");
12 | const [twitterAccount, setTwitterAccount] = useState("No connected account");
13 | const [twitterAccountLoading, setTwitterAccountLoading] = useState(false);
14 | const [composioApiKey, setComposioApiKey] = useState("");
15 | const [composioApiKeyLoading, setComposioApiKeyLoading] = useState(false);
16 |
17 | useEffect(() => {
18 | const getApiKey = async () => {
19 | const apiKey = await getComposioApiKey(user.uid);
20 | setComposioApiKey(apiKey);
21 | }
22 | getApiKey();
23 | checkConnectionStatus("TWITTER", setTwitterAccount, user.email.split("@")[0]);
24 | setUsername(user.email.split("@")[0]);
25 | }, [user.uid, checkConnectionStatus]);
26 |
27 | const handleChangeComposioApiKey = async () => {
28 | try {
29 | setComposioApiKeyLoading(true)
30 | await updateComposioApiKey(user.uid, composioApiKey);
31 | enqueueSnackbar("Composio API key updated successfully", { variant: 'success' });
32 | } catch (error) {
33 | enqueueSnackbar("Error updating Composio API key", { variant: 'error' });
34 | } finally {
35 | setComposioApiKeyLoading(false)
36 | }
37 | }
38 |
39 | const handleLinkTwitterAccount = async () => {
40 | await initiateTwitterConnectionAndConnectAdmin(user.email.split("@")[0], setTwitterAccountLoading);
41 | }
42 |
43 | return
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | };
53 |
54 | export default Settings;
55 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/utils/firebase_utils.js:
--------------------------------------------------------------------------------
1 | import { signInWithPopup, GoogleAuthProvider } from "firebase/auth";
2 | import { auth } from "../config/firebase";
3 | import { addUserData } from "../config/firebase";
4 |
5 | export const signUpWithGoogle = async (navigate) => {
6 | const provider = new GoogleAuthProvider();
7 | try {
8 | const result = await signInWithPopup(auth, provider);
9 | const user = result.user;
10 | await addUserData(user.uid, user.email.split("@")[0], user.email);
11 | navigate("/settings");
12 | } catch (error) {
13 | alert(error);
14 | console.error("Error during Google sign-up:", error);
15 | }
16 | };
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/src/utils/twitter_utils.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const getUserDataByUsername = async (username) => {
4 | const token = "Bearer " + import.meta.env.VITE_TWITTER_TOKEN
5 | try {
6 | const response = await axios.get(`/twitter/2/users/by/username/${username}`, {
7 | params: {
8 | 'user.fields': 'description,id,name,profile_image_url,username'
9 | },
10 | headers: {
11 | 'Authorization': token
12 | }
13 | });
14 | return response.data;
15 | } catch (error) {
16 | console.error('Error fetching user data:', error);
17 | throw error;
18 | }
19 | };
20 |
21 | export default getUserDataByUsername;
22 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | darkMode: 'class',
4 | theme: {
5 | extend: {
6 | colors: {
7 | primary: {"50":"#faf5ff","100":"#f3e8ff","200":"#e9d5ff","300":"#d8b4fe","400":"#c084fc","500":"#a855f7","600":"#9333ea","700":"#7e22ce","800":"#6b21a8","900":"#581c87","950":"#3b0764"}
8 | },
9 | keyframes: {
10 | glow: {
11 | '0%, 100%': { boxShadow: '0 0 1px #fde68a, 0 0 4px #fde68a' },
12 | '50%': { boxShadow: '0 0 8px #fde68a, 0 0 8px #fde68a' },
13 | }
14 | },
15 | animation: {
16 | glow: 'glow 1s ease-in-out infinite',
17 | }
18 | },
19 | fontFamily: {
20 | 'body': [
21 | 'Inter',
22 | 'ui-sans-serif',
23 | 'system-ui',
24 | '-apple-system',
25 | 'system-ui',
26 | 'Segoe UI',
27 | 'Roboto',
28 | 'Helvetica Neue',
29 | 'Arial',
30 | 'Noto Sans',
31 | 'sans-serif',
32 | 'Apple Color Emoji',
33 | 'Segoe UI Emoji',
34 | 'Segoe UI Symbol',
35 | 'Noto Color Emoji'
36 | ],
37 | 'sans': [
38 | 'Inter',
39 | 'ui-sans-serif',
40 | 'system-ui',
41 | '-apple-system',
42 | 'system-ui',
43 | 'Segoe UI',
44 | 'Roboto',
45 | 'Helvetica Neue',
46 | 'Arial',
47 | 'Noto Sans',
48 | 'sans-serif',
49 | 'Apple Color Emoji',
50 | 'Segoe UI Emoji',
51 | 'Segoe UI Symbol',
52 | 'Noto Color Emoji'
53 | ]
54 | }
55 | },
56 | content: [
57 | "./index.html",
58 | "./src/**/*.{js,ts,jsx,tsx}",
59 | "./node_modules/flowbite-react/lib/esm/**/*.js",
60 | "./node_modules/flowbite/**/*.js"
61 | ],
62 | plugins: [
63 | require('flowbite/plugin')
64 | ],
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | {"source": "/(.*)", "destination": "/"}
4 | ]
5 | }
--------------------------------------------------------------------------------
/twitter-assistant/twitter-retweet-helper/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | // proxy to twitter api
8 | server: {
9 | proxy: {
10 | '/twitter': {
11 | target: 'https://api.twitter.com',
12 | changeOrigin: true,
13 | rewrite: (path) => path.replace(/^\/twitter/, ''),
14 | },
15 | },
16 | },
17 | })
18 |
--------------------------------------------------------------------------------