Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
version: 2.1

jobs:
test:
docker:
- image: circleci/python:3.12
steps:
- checkout
- run:
name: Install uv
command: |
curl -sSL https://install.uv.tools | bash
- run:
name: Install dependencies
command: uv init # Installs dependencies from pyproject.toml
- run:
name: Run pytest tests
command: pytest tests
- run:
name: Run ruff for linting
command: ruff check .
- run:
name: Run mypy for type checking
command: mypy .

workflows:
version: 2
test_and_static_analysis:
jobs:
- test
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,18 @@ dmypy.json
# Cython debug symbols
cython_debug/

# Mac metadata file
.DS_Store

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.DS_Store

credentials.json
token.json
token_calendar.json
.python-version
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
160 changes: 159 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,159 @@
# mAIgic-nyu
# mAIgic

## Project Description

**mAIgic** is an AI-powered assistant designed to enhance productivity and organization by tracking messages and important information across multiple platforms. It identifies and reminds users about follow-ups, ensuring timely responses and efficient information management. With intelligent search capabilities, mAIgic enables users to easily locate conversations and details tied to specific individuals or tasks.

The project supports popular platforms such as Slack, Trello, Gmail, and Google Calendar, providing seamless follow-up tracking, reminders, and contextual search across these communication channels. This integration leverages AI to automate the reminder process, making it easier to stay on top of essential follow-ups and tasks.

## Features

- **Slack Integration**: Manage tasks, create checklists, and fetch emails directly from Slack.
- **Trello Integration**: Automate task management with Trello boards, lists, and cards.
- **Gmail Integration**: Fetch and post unread emails to Slack channels.
- **Google Calendar Integration**: Automatically synchronize tasks with due dates to Google Calendar.
- **Unit Testing**: Comprehensive test coverage using `pytest`.
- **Static Code Analysis**: Ensure code quality with `ruff` (linter).
- **Type Checking**: Maintain type consistency with `mypy`.
- **Continuous Integration**: CircleCI pipeline for running tests and static analysis on every push.

## Setup Instructions

### 1. Clone the Repository

```bash
git clone https://github.com/your-username/mAIgic.git
cd mAIgic
```

### 2. Install Dependencies

Using the uv package manager, run the following command:

```bash
uv sync
```

This will install all required dependencies in a virtual environment. To activate the virtual environment:

```bash
source ./.venv/bin/activate
```

## Running the Application

### 1. Start the Application

Run the application to start the Slack bot and Gmail bot:

```bash
python app.py
```

This will:
1. Start a thread for the Gmail bot to periodically fetch unread emails.
2. Initialize the Slack bot to listen for commands.

## Commands Overview

### Slack Commands

1. **Add Task to Trello**:
```
add "task name" to [list name] by YYYY-MM-DD HH:MM
```
- Adds a task to the specified Trello list.
- If a due date is provided, it creates a Google Calendar event.

2. **Remove Task from Trello**:
```
remove "task name" from [list name]
```
- Removes a task from the Trello list and deletes the corresponding Google Calendar event.

3. **Show Tasks in Trello List**:
```
show me tasks in [list name]
```
- Displays all tasks in the specified Trello list.

4. **Delete Trello List**:
```
delete list [list name]
```
- Archives the specified Trello list and removes associated Google Calendar events.

5. **Fetch Unread Emails**:
```
fetch emails
```
- Fetches the latest 5 unread emails and posts them in Slack.

6. **Show Specific Number of Emails**:
```
show me [number] emails
```
- Fetches the specified number of unread emails.

7. **Create Checklist in Trello Card**:
```
create checklist "checklist name" in "card name" in [list name]
```

8. **Add Item to Checklist**:
```
add "item name" to checklist "checklist name" in "card name" in [list name]
```

9. **Help**:
```
help
```
- Displays the list of available commands.

## Running Tests

### Unit Tests

Run tests using pytest:

```bash
pytest tests
```

### Static Code Analysis

Check for linting issues with ruff:

```bash
ruff check .
```

### Type Checking

Perform type checking with mypy:

```bash
mypy .
```

## CircleCI Configuration

The project uses CircleCI for continuous integration. Every push triggers the following steps:
1. Install dependencies using uv.
2. Run unit tests using pytest.
3. Perform static code analysis using ruff.
4. Perform type checking using mypy.

## License

This project is licensed under the MIT License. See the LICENSE file for details.

## Contributors

- Siddharth Singh - sms10221@nyu.edu
- Adittya Mittal - am14079@nyu.edu
- Anushka Tawte - at5849@nyu.edu
- Rafael de Leon - rdl404@nyu.edu
- Alex Ying - aty2009@nyu.edu
- Mridul Mittal - mm13171@nyu.edu
13 changes: 13 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# app.py

import threading
from src.slack.slack_bot import run_slack_bot
from src.gmail.gmail_bot import run_gmail_to_slack

if __name__ == "__main__":
# Start Gmail bot in a separate thread
gmail_thread = threading.Thread(target=run_gmail_to_slack)
gmail_thread.start()

# Start Slack bot
run_slack_bot()
6 changes: 6 additions & 0 deletions hello.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def main():
print("Hello from maigic!")


if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[mypy]
python_version = 3.10
ignore_missing_imports = True
15 changes: 15 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[project]
name = "mAIgic"
version = "0.1.0"
description = "A project focused on testing, static analysis, and CI integration with CircleCI"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"pytest",
"ruff",
"mypy"
]

[tool.ruff.lint]
select = ["ALL"]
ignore = ["D203", "D213", "COM812", "ISC001", "PLW0603", "FBT001", "FBT002", "SLF001", "ANN001", "ANN201", "PLR2004", "DTZ005", "SIM117", "D103", "BLE001"]
3 changes: 3 additions & 0 deletions src/calendar/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# src/calendar/__init__.py
from .calendar_api import authenticate_google_calendar, add_event_to_calendar, update_calendar_event, delete_calendar_event
from .calendar_sync import add_task_to_calendar, update_task_in_calendar, delete_task_from_calendar
107 changes: 107 additions & 0 deletions src/calendar/calendar_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# src/calendar/calendar_api.py

import os
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from google.auth.transport.requests import Request

# Load environment variables
SCOPES = ['https://www.googleapis.com/auth/calendar']

# Authenticate and set up Google Calendar API
def authenticate_google_calendar():
"""
Authenticate and set up the Google Calendar API client.

Checks for existing credentials in `token_calendar.json`. If credentials
are invalid or do not exist, initiates the OAuth2 flow to generate new credentials.

Returns:
google.oauth2.credentials.Credentials: Authenticated credentials for the API.

Raises:
FileNotFoundError: If `credentials.json` is not found in the current directory.
"""
creds = None
if os.path.exists('token_calendar.json'):
creds = Credentials.from_authorized_user_file('token_calendar.json', SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
with open('token_calendar.json', 'w') as token:
token.write(creds.to_json())
return creds

# Add an event to Google Calendar
def add_event_to_calendar(task_name: str, due_date: str) -> str:
"""
Add a new event to the user's primary Google Calendar.

Args:
task_name (str): The name or summary of the event.
due_date (str): The start and end time of the event in ISO 8601 format
(e.g., '2024-12-04T10:00:00-07:00').

Returns:
str: The unique event ID of the created event.

Raises:
googleapiclient.errors.HttpError: If the event creation fails.
"""
service = build('calendar', 'v3', credentials=authenticate_google_calendar())
event = {
'summary': task_name,
'start': {
'dateTime': due_date,
},
'end': {
'dateTime': due_date,
},
}
event = service.events().insert(calendarId='primary', body=event).execute()
return event['id']

# Update an existing event in Google Calendar
def update_calendar_event(event_id: str, task_name: str, due_date: str) -> dict:
"""
Update an existing event in the user's primary Google Calendar.

Args:
event_id (str): The unique ID of the event to be updated.
task_name (str): The updated name or summary of the event.
due_date (str): The updated start and end time of the event in ISO 8601 format.

Returns:
dict: The updated event resource.

Raises:
googleapiclient.errors.HttpError: If the event update fails.
"""
service = build('calendar', 'v3', credentials=authenticate_google_calendar())
event = service.events().get(calendarId='primary', eventId=event_id).execute()
event['summary'] = task_name
event['start']['dateTime'] = due_date
event['end']['dateTime'] = due_date
updated_event = service.events().update(calendarId='primary', eventId=event_id, body=event).execute()
return updated_event

# Delete an event from Google Calendar
def delete_calendar_event(event_id : str) -> None:
"""
Delete an event from the user's primary Google Calendar.

Args:
event_id (str): The unique ID of the event to be deleted.

Returns:
None

Raises:
googleapiclient.errors.HttpError: If the event deletion fails.
"""
service = build('calendar', 'v3', credentials=authenticate_google_calendar())
service.events().delete(calendarId='primary', eventId=event_id).execute()
Loading