diff --git a/.gitignore b/.gitignore index eef836e..acfb858 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ .env /__pycache__ -/calendar +/secret /dvenv copies.txt +.gitignore +user_location.db diff --git a/task_bot.py b/task_bot.py index bc0b6b4..fde8d74 100644 --- a/task_bot.py +++ b/task_bot.py @@ -2,7 +2,7 @@ import os import discord import datetime import pytz -import google_auth_oauthlib.flow +from google_auth_oauthlib.flow import InstalledAppFlow from weather import OpenWeatherMapAPIClient from googleapiclient.discovery import build from google.oauth2.credentials import Credentials @@ -15,9 +15,9 @@ load_dotenv() DISCORD_TOKEN = os.getenv("DISCORD_TOKEN") WEATHER_TOKEN = os.getenv("WEATHER_TOKEN") -CREDENTIALS_FILE = './calendar/credentials.json' -TOKEN_FILE = './calendar/token.json' -SCOPES = ['https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/tasks.readonly'] +CREDENTIALS_FILE = './secret/credentials.json' +TOKEN_FILE = './secret/token.json' +SCOPES = ['https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/tasks.readonly', 'https://www.googleapis.com/auth/tasks'] weather_client = OpenWeatherMapAPIClient(WEATHER_TOKEN, "MyDiscordWeatherBot") intents = discord.Intents(messages=True, guilds=True) @@ -32,8 +32,9 @@ def build_calendar_service(): return build('calendar', 'v3', credentials=creds) def build_tasks_service(): - creds = creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES) - return build('tasks', 'v1', credentials=creds) + creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES) + service = build('tasks', 'v1', credentials=creds) + return service, creds def parse_google_time(s: str) -> datetime.datetime: dt = datetime.datetime.fromisoformat(s) @@ -47,23 +48,6 @@ def parse_google_time(s: str) -> datetime.datetime: return dt.replace(tzinfo=datetime.timezone.utc) return dt -def is_due_today(due_iso: str, tz: pytz.timezone = TZ) -> bool: - if not due_iso: - return False - try: - dt = datetime.datetime.fromisoformat(due_iso.replace("Z", "+00:00")) - except Exception: - return False - return dt.astimezone(tz).date() == datetime.datetime.now(tz).date() - -def due_time_str(due_iso: str, tz: pytz.timezone = TZ) -> str: - if not due_iso: - return "" - try: - dt = datetime.datetime.fromisoformat(due_iso.replace("Z", "+00:00")).astimezone(tz) - return dt.strftime("%H:%M") - except Exception: - return "" #Calendar authentification async def authenticate(): @@ -72,43 +56,59 @@ async def authenticate(): return print("No token found: running interactive OAuth flow(will open browser).") - flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(CREDENTIALS_FILE, SCOPES) + flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_FILE, SCOPES) credentials = flow.run_local_server(port=0) with open(TOKEN_FILE, 'w') as f: f.write(credentials.to_json()) print(f"Saved credentials to {TOKEN_FILE} for instant access") + @bot.tree.command(name="daily_tasks", description="Check today's saved tasks") async def today(interaction: discord.Interaction): await interaction.response.defer(thinking=True) try: - service = build_tasks_service() + service, creds = build_tasks_service() tl_res = service.tasklists().list(maxResults=100).execute() - lists = tl_res.get("items", []) + lists = tl_res.get("items", []) or [] + embed = discord.Embed( title="Today's tasks", color=0x2ecc71, timestamp=datetime.datetime.now(tz=TZ) ) + added = 0 for tl in lists: - tl_id = tl["id"] - tl_title = tl.get("title", "Untitled") - tasks_res = service.tasks().list(tasklist=tl_id, showCompleted=True, maxResults=200).execute() - items = tasks_res.get("items", []) - if not items: - continue - + tl_id = tl.get("id") + tl_title = tl.get("title") or "" + + tasks_res = service.tasks().list(tasklist=tl_id, showCompleted=True, showHidden=True, maxResults=250).execute() or {} + items = tasks_res.get("items", []) or [] + lines = [] - for t in items: + for t in items: + if not isinstance(t, dict): + continue + status = "✅" if t.get("status") == "completed" else "🔲" - title = t.get("title", "(no title)") - lines.append(f"{status} {title}") + title = t.get("title") or "(no title)" + completed_ts = t.get("completed") + + if completed_ts: + completed_short = completed_ts.replace("T", "").split(".")[0] + lines.append(f"{status} {title} - completed {completed_short}") + else: + lines.append(f"{status} {title}") + + if not lines: + continue + value = "\n".join(lines)[:1024] embed.add_field(name=tl_title, value=value, inline=False) + added += 1 - if not embed.fields: + if added == 0: embed.description = "No tasks due today." await interaction.followup.send(embed=embed) @@ -157,10 +157,7 @@ async def events(interaction: discord.Interaction): if not events: - embed = discord.Embed( - title="No events this week!", - description="Go outside maybe" - ) + embed.description = "No tasks due today" await interaction.followup.send(embed=embed) return else: @@ -242,6 +239,7 @@ async def on_ready(): print("Registered Commands:") for command in commands: print(f"- {command.name}") + print("- !set_location") print("your bot is online and ready to serve !") bot.run(DISCORD_TOKEN)