tasks pas fini

This commit is contained in:
Tenzing Kandang 2026-03-24 22:49:57 +01:00
parent a5fd33774c
commit 3aab9eace2

View File

@ -1,43 +1,138 @@
import os import os
import discord import discord
import datetime import datetime
import pytz
import google_auth_oauthlib.flow import google_auth_oauthlib.flow
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 discord.ext import commands from discord.ext import commands
from dotenv import load_dotenv from dotenv import load_dotenv
from database import UserLocation
global service from asyncio import to_thread
service = None
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
load_dotenv() 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'
TOKEN_FILE = './calendar/token.json'
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/tasks.readonly']
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)
intents.message_content = True intents.message_content = True
bot = commands.Bot(command_prefix='!', intents=intents) bot = commands.Bot(command_prefix='!', intents=intents)
user_location_db = UserLocation()
TZ = pytz.timezone("Europe/Paris")
def build_calendar_service():
creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)
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)
def parse_google_time(s: str) -> datetime.datetime:
dt = datetime.datetime.fromisoformat(s)
if 'T' not in s:
return dt.replace(tzinfo=datetime.timezone.utc)
if s.endswith('Z'):
return datetime.datetime.fromisoformat(s.replace('Z', '+00:00'))
if dt.tzinfo is None:
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 #Calendar authentification
async def authenticate(): async def authenticate():
if os.path.exists(TOKEN_FILE):
print("Token file exists; skipping interactive authentication.")
return
print("Authenticating..") print("No token found: running interactive OAuth flow(will open browser).")
global service flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(CREDENTIALS_FILE, SCOPES)
flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file('./calendar/credentials.json', SCOPES)
credentials = flow.run_local_server(port=0) credentials = flow.run_local_server(port=0)
service = build('calendar', 'v3', credentials=credentials)
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()
try:
service = build_tasks_service()
tl_res = service.tasklists().list(maxResults=100).execute()
lists = tl_res.get("items", [])
embed = discord.Embed(
title="Today's tasks",
color=0x2ecc71,
timestamp=datetime.datetime.now(tz=TZ)
)
any_tasks = False
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", [])
day_tasks = [t for t in items if is_due_today(t.get("due"))]
if not day_tasks:
continue
any_tasks = True
lines = []
for t in sorted(day_tasks, key=lambda x: x.get("due") or ""):
status = "✅done" if t.get("status") == "completed" else "🔲"
time = due_time_str(t.get("due"))
title = t.get("title", "(no title)")
tid = t.get("id", "")
lines.append(f"{status} {title}{(' - ' + time) if time else ''} (id:{tid})")
value = "\n".join(lines)[:1024]
embed.add_field(name=tl_title, value=value, inline=False)
if not any_tasks:
embed.description = "No tasks due today."
await interaction.followup.send(embed=embed)
except Exception as e:
await interaction.followup.send(f"An error occured:{e}")
@bot.tree.command(name='weekly_events') @bot.tree.command(name='weekly_events', description="Check this weeks saved events")
async def events(interaction: discord.Interaction): async def events(interaction: discord.Interaction):
await interaction.response.defer(thinking=True) await interaction.response.defer(thinking=True)
now = datetime.datetime.utcnow() try:
service = build_calendar_service()
except FileNotFoundError:
await interaction.followup.send("Calendar credentials not found.")
return
except Exception as e:
await interaction.followup.send(f"Failed to build service: {e}")
return
now = datetime.datetime.now(datetime.timezone.utc)
current_weekday = now.weekday() current_weekday = now.weekday()
start_of_week = now - datetime.timedelta(days=current_weekday) start_of_week = now - datetime.timedelta(days=current_weekday)
@ -46,22 +141,34 @@ async def events(interaction: discord.Interaction):
end_of_week = start_of_week + datetime.timedelta(days=6) end_of_week = start_of_week + datetime.timedelta(days=6)
end_of_week= end_of_week.replace(hour=23, minute=59, second=59, microsecond=999999) end_of_week= end_of_week.replace(hour=23, minute=59, second=59, microsecond=999999)
print(f"Start of week: {start_of_week.isoformat()}, End of week: {end_of_week.isoformat()}")
try: try:
events_result = service.events().list( events_result = service.events().list(
calendarId='primary', calendarId='primary',
timeMin=start_of_week.isoformat() + 'Z', timeMin=start_of_week.isoformat(),
timeMax=end_of_week.isoformat() + 'Z', timeMax=end_of_week.isoformat(),
singleEvents=True, singleEvents=True,
orderBy='startTime' orderBy='startTime'
).execute() ).execute()
print(f"API Response: {events_result}")
events = events_result.get('items',[]) events = events_result.get('items',[])
if not events: if not events:
await interaction.response.send_message('No events this week !') embed = discord.Embed(
title="No events this week!",
description="Go outside maybe"
)
await interaction.followup.send(embed=embed)
return
else: else:
embed = discord.Embed( embed = discord.Embed(
title="📅 Weekly Events Summary", title="📅 Weekly Events Summary",
description="This week's events",
color=discord.Color.blue() color=discord.Color.blue()
) )
@ -73,7 +180,9 @@ async def events(interaction: discord.Interaction):
event_time = datetime.datetime.fromisoformat(event_time_str) event_time = datetime.datetime.fromisoformat(event_time_str)
event_time_local = event_time.strftime("%A, %B %d, %Y, %H:%M") event_time_local = event_time.strftime("%A, %B %d, %Y, %H:%M")
task = f"**{event['summary']}**\n⏰ **At: {event_time_local}**"
event_description = event.get('description')
task = f"**{event['summary']}**\n{event_time_local}\n Description: {event_description}"
embed.add_field(name="\u200b", value=task, inline=False) embed.add_field(name="\u200b", value=task, inline=False)
await interaction.followup.send(embed=embed) await interaction.followup.send(embed=embed)
@ -83,8 +192,21 @@ async def events(interaction: discord.Interaction):
#Weather commad tree #Weather commad tree
@bot.tree.command(name="weather") @bot.command(name="set_location")
async def current_weather(interaction: discord.Interaction, location: str): async def set_location(ctx, *, location: str):
user_id = ctx.author.id
user_location_db.set_user_location(user_id, location)
await ctx.send(f"Location set to: {location}")
@bot.tree.command(name="weather", description="Check the weather!")
async def current_weather(interaction: discord.Interaction, location: str = None):
user_id = interaction.user.id
if location is None:
location = user_location_db.get_user_location(user_id)
if not location:
await interaction.response.send_message("Please provide a location or set one using '!set_location <location>'.\n(Your location will be stored in a database for ease of access)")
return
print(f"Received weather command from {interaction.user.display_name}") # Log intéraction print(f"Received weather command from {interaction.user.display_name}") # Log intéraction
@ -93,36 +215,22 @@ async def current_weather(interaction: discord.Interaction, location: str):
print(f"JSON API {current_weather}") print(f"JSON API {current_weather}")
# Check that current_weather is a dictionary and contains necessary keys # Check that current_weather is a dictionary and contains necessary keys
if not isinstance(current_weather, dict) or 'main' not in current_weather or 'weather' not in current_weather: if isinstance(current_weather, dict) and 'main' in current_weather and 'weather' in current_weather:
await interaction.response.send_message("Could not retrieve weather data. Please check the location.") weather_condition = current_weather['weather'][0]['main']
return
weather_list = current_weather['weather']
if len(weather_list) == 0:
await interaction.response.send_message("Weather information is not available.")
return
weather_condition = weather_list[0]['main']
temp = current_weather['main']['temp'] temp = current_weather['main']['temp']
icon = current_weather['weather'][0]['icon']
icon = current_weather['weather']['icon']
embed = discord.Embed( embed = discord.Embed(
title=f"Current weather in {location}", title=f"Current weather in {location}",
description=f"Temperature: {temp}°C\nSky: {weather_condition}", description=f"Temperature: {temp}°C\nWeather condition: {weather_condition}",
color=discord.Color.yellow()
) )
embed.set_thumbnail(url=f"https://openweathermap.org/img/wn/{icon}.png") embed.set_thumbnail(url=f"https://openweathermap.org/img/wn/{icon}.png")
await interaction.response.send_message(embed=embed) await interaction.response.send_message(embed=embed)
else:
@bot.tree.command(name='hello') await interaction.response.send_message("Could not retrieve weather data. Please check the location or try again. ")
async def hello_command(interaction: discord.Interaction):
print(f"Received hello command from {interaction.user.display_name}") # Log intéraction
user_nick = interaction.user.display_name
await interaction.response.send_message(f'Hello {user_nick}!')
@bot.event @bot.event
async def on_ready(): async def on_ready():
@ -135,5 +243,6 @@ 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("your bot is online and ready to serve !")
bot.run(DISCORD_TOKEN) bot.run(DISCORD_TOKEN)