basically refactored the email viewer
This commit is contained in:
7
apis/__init__.py
Normal file
7
apis/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
APIs package for the GTD Terminal Tools project.
|
||||
|
||||
This package contains modules for interacting with various external services like:
|
||||
- Himalaya email client
|
||||
- Taskwarrior task manager
|
||||
"""
|
||||
21
apis/himalaya/__init__.py
Normal file
21
apis/himalaya/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
Himalaya API module for interacting with the Himalaya email client.
|
||||
"""
|
||||
|
||||
from apis.himalaya.client import (
|
||||
list_envelopes,
|
||||
list_accounts,
|
||||
list_folders,
|
||||
delete_message,
|
||||
archive_message,
|
||||
get_message_content,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"list_envelopes",
|
||||
"list_accounts",
|
||||
"list_folders",
|
||||
"delete_message",
|
||||
"archive_message",
|
||||
"get_message_content",
|
||||
]
|
||||
169
apis/himalaya/client.py
Normal file
169
apis/himalaya/client.py
Normal file
@@ -0,0 +1,169 @@
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
from typing import Tuple, List, Dict, Any, Optional, Union
|
||||
|
||||
async def list_envelopes(limit: int = 9999) -> Tuple[List[Dict[str, Any]], bool]:
|
||||
"""
|
||||
Retrieve a list of email envelopes using the Himalaya CLI.
|
||||
|
||||
Args:
|
||||
limit: Maximum number of envelopes to retrieve
|
||||
|
||||
Returns:
|
||||
Tuple containing:
|
||||
- List of envelope dictionaries
|
||||
- Success status (True if operation was successful)
|
||||
"""
|
||||
try:
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
f"himalaya envelope list -o json -s {limit}",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
envelopes = json.loads(stdout.decode())
|
||||
return envelopes, True
|
||||
else:
|
||||
logging.error(f"Error listing envelopes: {stderr.decode()}")
|
||||
return [], False
|
||||
except Exception as e:
|
||||
logging.error(f"Exception during envelope listing: {e}")
|
||||
return [], False
|
||||
|
||||
async def list_accounts() -> Tuple[List[Dict[str, Any]], bool]:
|
||||
"""
|
||||
Retrieve a list of accounts configured in Himalaya.
|
||||
|
||||
Returns:
|
||||
Tuple containing:
|
||||
- List of account dictionaries
|
||||
- Success status (True if operation was successful)
|
||||
"""
|
||||
try:
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
"himalaya account list -o json",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
accounts = json.loads(stdout.decode())
|
||||
return accounts, True
|
||||
else:
|
||||
logging.error(f"Error listing accounts: {stderr.decode()}")
|
||||
return [], False
|
||||
except Exception as e:
|
||||
logging.error(f"Exception during account listing: {e}")
|
||||
return [], False
|
||||
|
||||
async def list_folders() -> Tuple[List[Dict[str, Any]], bool]:
|
||||
"""
|
||||
Retrieve a list of folders available in Himalaya.
|
||||
|
||||
Returns:
|
||||
Tuple containing:
|
||||
- List of folder dictionaries
|
||||
- Success status (True if operation was successful)
|
||||
"""
|
||||
try:
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
"himalaya folder list -o json",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
folders = json.loads(stdout.decode())
|
||||
return folders, True
|
||||
else:
|
||||
logging.error(f"Error listing folders: {stderr.decode()}")
|
||||
return [], False
|
||||
except Exception as e:
|
||||
logging.error(f"Exception during folder listing: {e}")
|
||||
return [], False
|
||||
|
||||
async def delete_message(message_id: int) -> bool:
|
||||
"""
|
||||
Delete a message by its ID.
|
||||
|
||||
Args:
|
||||
message_id: The ID of the message to delete
|
||||
|
||||
Returns:
|
||||
True if deletion was successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
f"himalaya message delete {message_id}",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
return process.returncode == 0
|
||||
except Exception as e:
|
||||
logging.error(f"Exception during message deletion: {e}")
|
||||
return False
|
||||
|
||||
async def archive_message(message_id: int) -> bool:
|
||||
"""
|
||||
Archive a message by its ID.
|
||||
|
||||
Args:
|
||||
message_id: The ID of the message to archive
|
||||
|
||||
Returns:
|
||||
True if archiving was successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
f"himalaya message archive {message_id}",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
return process.returncode == 0
|
||||
except Exception as e:
|
||||
logging.error(f"Exception during message archiving: {e}")
|
||||
return False
|
||||
|
||||
async def get_message_content(message_id: int, format: str = "html") -> Tuple[Optional[str], bool]:
|
||||
"""
|
||||
Retrieve the content of a message by its ID.
|
||||
|
||||
Args:
|
||||
message_id: The ID of the message to retrieve
|
||||
format: The desired format of the message content ("html" or "text")
|
||||
|
||||
Returns:
|
||||
Tuple containing:
|
||||
- Message content (or None if retrieval failed)
|
||||
- Success status (True if operation was successful)
|
||||
"""
|
||||
try:
|
||||
cmd = f"himalaya message read {message_id}"
|
||||
if format == "text":
|
||||
cmd += " -t"
|
||||
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
content = stdout.decode()
|
||||
return content, True
|
||||
else:
|
||||
logging.error(f"Error retrieving message content: {stderr.decode()}")
|
||||
return None, False
|
||||
except Exception as e:
|
||||
logging.error(f"Exception during message content retrieval: {e}")
|
||||
return None, False
|
||||
@@ -59,6 +59,6 @@ def get_access_token(scopes):
|
||||
f.write(cache.serialize())
|
||||
|
||||
access_token = token_response['access_token']
|
||||
headers = {'Authorization': f'Bearer {access_token}', 'Prefer': 'outlook.body-content-type="text"'}
|
||||
headers = {'Authorization': f'Bearer {access_token}', 'Prefer': 'outlook.body-content-type="text",IdType="ImmutableId"'}
|
||||
|
||||
return access_token, headers
|
||||
|
||||
@@ -152,7 +152,7 @@ async def delete_mail_async(maildir_path, headers, progress, task_id, dry_run=Fa
|
||||
progress.console.print(f"[DRY-RUN] Would delete message: {message_id}")
|
||||
progress.advance(task_id)
|
||||
|
||||
async def synchronize_maildir_async(maildir_path, headers, progress, task_id, dry_run=False):
|
||||
async def synchronize_maildir_async(maildir_path, headers, progress, task_id, dry_run=False):
|
||||
"""
|
||||
Synchronize Maildir with Microsoft Graph API.
|
||||
|
||||
|
||||
17
apis/taskwarrior/__init__.py
Normal file
17
apis/taskwarrior/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
Taskwarrior API module for interacting with the Taskwarrior command-line task manager.
|
||||
"""
|
||||
|
||||
from apis.taskwarrior.client import (
|
||||
create_task,
|
||||
list_tasks,
|
||||
complete_task,
|
||||
delete_task,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"create_task",
|
||||
"list_tasks",
|
||||
"complete_task",
|
||||
"delete_task",
|
||||
]
|
||||
146
apis/taskwarrior/client.py
Normal file
146
apis/taskwarrior/client.py
Normal file
@@ -0,0 +1,146 @@
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
from typing import Tuple, List, Dict, Any, Optional, Union
|
||||
|
||||
async def create_task(task_description: str, tags: List[str] = None, project: str = None,
|
||||
due: str = None, priority: str = None) -> Tuple[bool, Optional[str]]:
|
||||
"""
|
||||
Create a new task using the Taskwarrior CLI.
|
||||
|
||||
Args:
|
||||
task_description: Description of the task
|
||||
tags: List of tags to apply to the task
|
||||
project: Project to which the task belongs
|
||||
due: Due date in the format that Taskwarrior accepts
|
||||
priority: Priority of the task (H, M, L)
|
||||
|
||||
Returns:
|
||||
Tuple containing:
|
||||
- Success status (True if operation was successful)
|
||||
- Task ID or error message
|
||||
"""
|
||||
try:
|
||||
cmd = ["task", "add"]
|
||||
|
||||
# Add project if specified
|
||||
if project:
|
||||
cmd.append(f"project:{project}")
|
||||
|
||||
# Add tags if specified
|
||||
if tags:
|
||||
for tag in tags:
|
||||
cmd.append(f"+{tag}")
|
||||
|
||||
# Add due date if specified
|
||||
if due:
|
||||
cmd.append(f"due:{due}")
|
||||
|
||||
# Add priority if specified
|
||||
if priority and priority in ["H", "M", "L"]:
|
||||
cmd.append(f"priority:{priority}")
|
||||
|
||||
# Add task description
|
||||
cmd.append(task_description)
|
||||
|
||||
# Convert command list to string
|
||||
cmd_str = " ".join(cmd)
|
||||
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
cmd_str,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
return True, stdout.decode().strip()
|
||||
else:
|
||||
error_msg = stderr.decode().strip()
|
||||
logging.error(f"Error creating task: {error_msg}")
|
||||
return False, error_msg
|
||||
except Exception as e:
|
||||
logging.error(f"Exception during task creation: {e}")
|
||||
return False, str(e)
|
||||
|
||||
async def list_tasks(filter_str: str = "") -> Tuple[List[Dict[str, Any]], bool]:
|
||||
"""
|
||||
List tasks from Taskwarrior.
|
||||
|
||||
Args:
|
||||
filter_str: Optional filter string to pass to Taskwarrior
|
||||
|
||||
Returns:
|
||||
Tuple containing:
|
||||
- List of task dictionaries
|
||||
- Success status (True if operation was successful)
|
||||
"""
|
||||
try:
|
||||
cmd = f"task {filter_str} export"
|
||||
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
tasks = json.loads(stdout.decode())
|
||||
return tasks, True
|
||||
else:
|
||||
logging.error(f"Error listing tasks: {stderr.decode()}")
|
||||
return [], False
|
||||
except Exception as e:
|
||||
logging.error(f"Exception during task listing: {e}")
|
||||
return [], False
|
||||
|
||||
async def complete_task(task_id: str) -> bool:
|
||||
"""
|
||||
Mark a task as completed.
|
||||
|
||||
Args:
|
||||
task_id: ID of the task to complete
|
||||
|
||||
Returns:
|
||||
True if task was completed successfully, False otherwise
|
||||
"""
|
||||
try:
|
||||
cmd = f"echo 'yes' | task {task_id} done"
|
||||
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
return process.returncode == 0
|
||||
except Exception as e:
|
||||
logging.error(f"Exception during task completion: {e}")
|
||||
return False
|
||||
|
||||
async def delete_task(task_id: str) -> bool:
|
||||
"""
|
||||
Delete a task.
|
||||
|
||||
Args:
|
||||
task_id: ID of the task to delete
|
||||
|
||||
Returns:
|
||||
True if task was deleted successfully, False otherwise
|
||||
"""
|
||||
try:
|
||||
cmd = f"echo 'yes' | task {task_id} delete"
|
||||
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
return process.returncode == 0
|
||||
except Exception as e:
|
||||
logging.error(f"Exception during task deletion: {e}")
|
||||
return False
|
||||
Reference in New Issue
Block a user