From 474d5d7e962a2cc586050ecf2f6d4eb402c48ba3 Mon Sep 17 00:00:00 2001 From: David Stephens Date: Mon, 24 Jun 2024 11:13:28 +0100 Subject: [PATCH 1/2] Fix some types in storage --- botto/storage/reminder_storage.py | 2 +- botto/storage/storage.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/botto/storage/reminder_storage.py b/botto/storage/reminder_storage.py index ec5bab4..a06f0f8 100644 --- a/botto/storage/reminder_storage.py +++ b/botto/storage/reminder_storage.py @@ -1,7 +1,7 @@ import logging from typing import AsyncGenerator from datetime import datetime -from typing import Optional, Any +from typing import Optional from aiohttp import ClientSession diff --git a/botto/storage/storage.py b/botto/storage/storage.py index b7b55b9..64a67aa 100644 --- a/botto/storage/storage.py +++ b/botto/storage/storage.py @@ -68,7 +68,7 @@ async def _iterate( sort: Optional[list[str]] = None, session: Optional[ClientSession] = None, fields: Optional[Union[list[str], str]] = None, - ) -> AsyncGenerator[dict]: + ) -> AsyncGenerator[dict, None]: params = {} if filter_by_formula: params = {"filterByFormula": filter_by_formula} From ca69022bc8567632f20b074e91cf017224002811 Mon Sep 17 00:00:00 2001 From: David Stephens Date: Mon, 24 Jun 2024 11:21:35 +0100 Subject: [PATCH 2/2] SI-38 Fix removal of beta testers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not sure how that ever worked in testing… --- botto/mixins/reaction_roles.py | 81 +++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/botto/mixins/reaction_roles.py b/botto/mixins/reaction_roles.py index 1055675..fb0d6d3 100644 --- a/botto/mixins/reaction_roles.py +++ b/botto/mixins/reaction_roles.py @@ -4,7 +4,7 @@ from collections import namedtuple from datetime import datetime, timedelta from functools import partial -from typing import Optional +from typing import Optional, AsyncGenerator from weakref import WeakValueDictionary import discord @@ -733,7 +733,8 @@ async def handle_removal_reaction( ) return False - await self.remove_tester_from_group(payload, tester) + await self.remove_tester_from_app_store(payload, tester) + message.add_reaction("🚪") return True async def handle_reaction(self, payload: discord.RawReactionActionEvent) -> bool: @@ -835,7 +836,7 @@ async def add_tester_to_group( ) raise - async def remove_tester_from_group( + async def remove_tester_from_app_store( self, payload: discord.RawReactionActionEvent, tester: Tester, @@ -868,31 +869,37 @@ async def remove_tester_from_group( ) return - app_store_tester = testers_with_email[0] + for app_store_tester in testers_with_email: + if app and app.beta_group_id not in app_store_tester.beta_group_ids: + msg = f"{tester.email} not in group {app.beta_group_id}, removal unnecessary" + log.info(msg) + return - if app.beta_group_id not in app_store_tester.beta_group_ids: - msg = f"{tester.email} not in group {app.beta_group_id}, removal unnecessary" - log.info(msg) - return - await self.app_store_connect_client.delete_beta_tester( - app_store_tester.id, app - ) - log.info(f"Removed {tester} from Beta Testers") - apps = await self.testflight_storage.find_apps_by_beta_group( - *app_store_tester.beta_group_ids - ) - records_to_update = [ - r - async for r in self.testflight_storage.list_requests( - tester_id=str(payload.user_id), - app_ids=[app.id for app in apps], - approval_filter=RequestApprovalFilter.APPROVED, - exclude_removed=True, + apps = await self.testflight_storage.find_apps_by_beta_group( + *app_store_tester.beta_group_ids ) - ] - for r in records_to_update: - r.removed = True - await self.testflight_storage.update_requests(records_to_update) + apps_by_key = {a.app_store_key_id: a for a in apps} + async with asyncio.TaskGroup() as g: + for app in apps_by_key.values(): + g.create_task( + self.app_store_connect_client.delete_beta_tester( + app_store_tester.id, app + ) + ) + + log.info(f"Removed {tester} from Beta Testers") + records_to_update = [ + r + async for r in self.testflight_storage.list_requests( + tester_id=str(payload.user_id), + app_ids=[app.id for app in apps], + approval_filter=RequestApprovalFilter.APPROVED, + exclude_removed=True, + ) + ] + for r in records_to_update: + r.removed = True + await self.testflight_storage.update_requests(records_to_update) except AppStoreConnectError as error: channel = self.get_channel(payload.channel_id) message = channel.get_partial_message(payload.message_id) @@ -940,11 +947,9 @@ async def on_raw_member_remove(self, payload: discord.RawMemberRemoveEvent): return user_testing_apps = [ - (await self.testflight_storage.fetch_app(r.app)).name - async for r in self.testflight_storage.list_requests( - tester_id=str(payload.user.id), - exclude_removed=True, - approval_filter=RequestApprovalFilter.APPROVED, + t.name + async for t in await self.fetch_apps_for_tester( + payload.user.id, RequestApprovalFilter.APPROVED ) ] if len(user_testing_apps) == 0: @@ -978,3 +983,17 @@ async def on_raw_member_remove(self, payload: discord.RawMemberRemoveEvent): await exit_notification_channel.send( f"Failed to update tester {tester} with leave message ID: {e}" ) + + async def fetch_apps_for_tester( + self, + user_id: str | int, + approval_filter: RequestApprovalFilter = RequestApprovalFilter.ALL, + ) -> AsyncGenerator[model.App, None]: + return ( + await self.testflight_storage.fetch_app(r.app) + async for r in self.testflight_storage.list_requests( + tester_id=str(user_id), + exclude_removed=True, + approval_filter=approval_filter, + ) + )