can now see completed tasks

This commit is contained in:
Tenzing Kandang 2026-03-26 23:26:12 +01:00
parent 1112cd83bf
commit 0cdfa8eefc
2 changed files with 42 additions and 42 deletions

4
.gitignore vendored
View File

@ -1,6 +1,8 @@
.env .env
/__pycache__ /__pycache__
/calendar /secret
/dvenv /dvenv
copies.txt copies.txt
.gitignore
user_location.db

View File

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