diff --git a/bot/__pycache__/task.cpython-313.pyc b/bot/__pycache__/task.cpython-313.pyc index 7bd93ad..51046a5 100644 Binary files a/bot/__pycache__/task.cpython-313.pyc and b/bot/__pycache__/task.cpython-313.pyc differ diff --git a/bot/task.py b/bot/task.py index e63ce8d..3a58d0f 100644 --- a/bot/task.py +++ b/bot/task.py @@ -7,7 +7,6 @@ class TaskSelect(Select): min_values=1, max_values=1, options=options) async def callback(self, interaction: discord.Interaction): - payload = json.loads(self.values[0]) tasklist_id = payload["tasklist_id"] task_id = payload["task_id"] diff --git a/main.py b/main.py index 53a4ebb..2ac6672 100644 --- a/main.py +++ b/main.py @@ -9,7 +9,6 @@ from bot.database import UserLocation from asyncio import to_thread from discord import app_commands from discord.ui import View, Select -from bot.task import TaskSelect, TasksView load_dotenv() DISCORD_TOKEN = os.getenv("DISCORD_TOKEN") @@ -63,6 +62,49 @@ async def authenticate(): f.write(credentials.to_json()) print(f"Saved credentials to {TOKEN_FILE} for instant access") +#Marl task as completed +class TaskSelect(Select): + def __init__(self, options): + super().__init__(placeholder="Choose a task to complete...", + min_values=1, max_values=1, options=options) + + async def callback(self, interaction: discord.Interaction): + payload = json.loads(self.values[0]) + tasklist_id = payload["tasklist_id"] + task_id = payload["task_id"] + + await interaction.response.defer(thinking=True) + try: + service = build_tasks_service() + task = await asyncio.to_thread(lambda: + service.tasks().get(tasklist=tasklist_id, task=task_id).execute()) + if not task: + await interaction.followup.send("Task not found.", ephemeral=True) + return + + if task.get("status") == "completed": + await interaction.followup.send("Task already completed.", ephemeral=True) + return + + now = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + body = dict(task) + body["status"] = "completed" + body["completed"] = now + + updated = await asyncio.to_thread(lambda: service.tasks().update(tasklist=tasklist_id, task=task_id, body=body).execute()) + + if updated.get("status") == "completed": + await interaction.followup.send(f"User: {interaction.user.display_name} marked task: '{updated.get('title')}' as completed.", ephemeral=True) + print(f"User {interaction.user.display_name} just completed task {updated.get('title')}") + + except Exception as e: + await interaction.followup.send(f"Error: {e}", ephemeral=True) + +class TasksView(View): + def __init__(self, options, timeout=120): + super().__init__(timeout=timeout) + self.add_item(TaskSelect(options)) + @bot.tree.command(name="daily_tasks", description="Check today's saved tasks and complete them") async def today(interaction: discord.Interaction): @@ -83,7 +125,7 @@ async def today(interaction: discord.Interaction): ) options = [] - added = 0 + total = 0 for tl in lists: tl_id = tl.get("id") @@ -103,20 +145,21 @@ async def today(interaction: discord.Interaction): if due_date != today and completed_date != today: continue - + total += 1 status = "✅" if t.get("status") == "completed" else "🔲" t_id = t.get("id") title = t.get("title") or "(no title)" - embed.add_field(name=f"{tl_title} - {status} {title}", value=f"ID: {t.get('id')}", inline=False) + embed.add_field(name=f"{tl_title} - {status} {title}", value=f"ID: {t_id}", inline=False) - value = json.dumps({"tasklist_id": tl_id, "task_id": t.get("id")}) - options.append(discord.SelectOption(label=(title[:90] or "(no title)"), description=tl_title[:50], value=value)) - added += 1 + value = json.dumps({"tasklist_id": tl_id, "task_id": t_id}) + options.append(discord.SelectOption(label=(title[:90] or "(no title)"), description=tl_title[:50], value=value)) + - if added == 0: - embed.description = "No tasks due today." + if total == 0: + await interaction.followup.send("No tasks due today.") return + options = options[:25] view = TasksView(options, interaction.user.id) await interaction.followup.send(embed=embed, view=view) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8e8c4f6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,42 @@ +aiohappyeyeballs==2.6.1 +aiohttp==3.13.3 +aiosignal==1.4.0 +attrs==25.4.0 +audioop-lts==0.2.2 +certifi==2026.2.25 +cffi==2.0.0 +charset-normalizer==3.4.5 +cryptography==46.0.5 +davey==0.1.0 +discord==2.3.2 +discord.py==2.7.1 +frozenlist==1.8.0 +geographiclib==2.1 +geopy==2.4.1 +google-api-core==2.30.0 +google-api-python-client==2.192.0 +google-auth==2.49.1 +google-auth-httplib2==0.3.0 +google-auth-oauthlib==1.3.0 +googleapis-common-protos==1.73.0 +httplib2==0.31.2 +idna==3.11 +multidict==6.7.1 +oauthlib==3.3.1 +propcache==0.4.1 +proto-plus==1.27.1 +protobuf==6.33.5 +pyasn1==0.6.3 +pyasn1_modules==0.4.2 +pycparser==3.0 +PyNaCl==1.5.0 +pyparsing==3.3.2 +python-dateutil==2.9.0.post0 +python-dotenv==1.2.2 +pytz==2026.1.post1 +requests==2.32.5 +requests-oauthlib==2.0.0 +six==1.17.0 +uritemplate==4.2.0 +urllib3==2.6.3 +yarl==1.22.0