33import asyncio
44import logging
55from datetime import datetime , timezone
6- from typing import Union , List , Optional , TYPE_CHECKING , Literal , NamedTuple
6+ from typing import Union , List , Optional , TYPE_CHECKING , Literal , NamedTuple , Dict
77from functools import wraps
88import json
99
4747 "set_default_balance" ,
4848 "AbortPurchase" ,
4949 "cost" ,
50+ "PaydayClaimInformation" ,
51+ "BankSetBalanceInformation" ,
52+ "BankTransferInformation" ,
53+ "BankPruneInformation" ,
5054)
5155
5256_MAX_BALANCE = 2 ** 63 - 1
8286_cache_is_global = None
8387_cache = {"bank_name" : None , "currency" : None , "default_balance" : None , "max_balance" : None }
8488
89+ _bot_ref : Optional [Red ] = None
8590
86- async def _init ():
91+
92+ async def _init (bot : Red ):
8793 global _config
94+ global _bot_ref
95+ _bot_ref = bot
8896 _config = Config .get_conf (None , 384734293238749 , cog_name = "Bank" , force_registration = True )
8997 _config .register_global (** _DEFAULT_GLOBAL )
9098 _config .register_guild (** _DEFAULT_GUILD )
@@ -337,6 +345,7 @@ async def set_balance(member: Union[discord.Member, discord.User], amount: int)
337345 group = _config .user (member )
338346 else :
339347 group = _config .member (member )
348+ old_balance = await group .balance ()
340349 await group .balance .set (amount )
341350
342351 if await group .created_at () == 0 :
@@ -345,7 +354,8 @@ async def set_balance(member: Union[discord.Member, discord.User], amount: int)
345354
346355 if await group .name () == "" :
347356 await group .name .set (member .display_name )
348-
357+ payload = BankSetBalanceInformation (member , guild , old_balance , amount )
358+ _bot_ref .dispatch ("red_bank_set_balance" , payload )
349359 return amount
350360
351361
@@ -484,8 +494,11 @@ async def transfer_credits(
484494 user = to .display_name , max_balance = max_bal , currency_name = currency
485495 )
486496
487- await withdraw_credits (from_ , amount )
488- return await deposit_credits (to , amount )
497+ sender_new = await withdraw_credits (from_ , amount )
498+ recipient_new = await deposit_credits (to , amount )
499+ payload = BankTransferInformation (from_ , to , guild , amount , sender_new , recipient_new )
500+ _bot_ref .dispatch ("red_bank_transfer_credits" , payload )
501+ return recipient_new
489502
490503
491504async def wipe_bank (guild : Optional [discord .Guild ] = None ) -> None :
@@ -500,8 +513,10 @@ async def wipe_bank(guild: Optional[discord.Guild] = None) -> None:
500513 """
501514 if await is_global ():
502515 await _config .clear_all_users ()
516+ _bot_ref .dispatch ("red_bank_wipe" , - 1 )
503517 else :
504518 await _config .clear_all_members (guild )
519+ _bot_ref .dispatch ("red_bank_wipe" , getattr (guild , "id" , None ))
505520
506521
507522async def bank_prune (bot : Red , guild : discord .Guild = None , user_id : int = None ) -> None :
@@ -524,46 +539,49 @@ async def bank_prune(bot: Red, guild: discord.Guild = None, user_id: int = None)
524539 If guild is :code:`None` and the bank is Local.
525540
526541 """
527-
528542 global_bank = await is_global ()
543+ if not global_bank and guild is None :
544+ raise BankPruneError ("'guild' can't be None when pruning a local bank" )
529545
546+ _guilds = set ()
547+ _uguilds = set ()
530548 if global_bank :
531- _guilds = set ()
532- _uguilds = set ()
549+ group = _config ._get_base_group (_config .USER )
533550 if user_id is None :
534551 async for g in AsyncIter (bot .guilds , steps = 100 ):
535552 if not g .unavailable and g .large and not g .chunked :
536553 _guilds .add (g )
537554 elif g .unavailable :
538555 _uguilds .add (g )
539- group = _config ._get_base_group (_config .USER )
540556
541557 else :
542- if guild is None :
543- raise BankPruneError ("'guild' can't be None when pruning a local bank" )
544- if user_id is None :
545- _guilds = {guild } if not guild .unavailable and guild .large else set ()
546- _uguilds = {guild } if guild .unavailable else set ()
547558 group = _config ._get_base_group (_config .MEMBER , str (guild .id ))
548-
549- if user_id is None :
550- for _guild in _guilds :
551- await _guild .chunk ()
552- accounts = await group .all ()
553- tmp = accounts .copy ()
554- members = bot .get_all_members () if global_bank else guild .members
555- user_list = {str (m .id ) for m in members if m .guild not in _uguilds }
556-
557- async with group .all () as bank_data : # FIXME: use-config-bulk-update
558559 if user_id is None :
559- for acc in tmp :
560- if acc not in user_list :
561- del bank_data [acc ]
560+ if guild .large and not guild .unavailable :
561+ _guilds .add (guild )
562+ if guild .unavailable :
563+ _uguilds .add (guild )
564+
565+ pruned = {}
566+ async with group .all () as bank_data :
567+ if user_id in None :
568+ for _guild in _guilds :
569+ await _guild .chunk ()
570+ members = bot .get_all_members () if global_bank else guild .members
571+ valid_users = {str (m .id ) for m in members if m .guild not in _uguilds }
572+ for account_user_id , account in bank_data .copy ().items ():
573+ if account_user_id not in valid_users :
574+ pruned [account_user_id ] = account
575+ del bank_data [account_user_id ]
562576 else :
563577 user_id = str (user_id )
564578 if user_id in bank_data :
579+ pruned [user_id ] = bank_data [user_id ]
565580 del bank_data [user_id ]
566581
582+ payload = BankPruneInformation (guild , user_id , pruned )
583+ _bot_ref .dispatch ("red_bank_prune" , payload )
584+
567585
568586async def get_leaderboard (positions : int = None , guild : discord .Guild = None ) -> List [tuple ]:
569587 """
@@ -725,11 +743,14 @@ async def set_global(global_: bool) -> bool:
725743
726744 if await is_global ():
727745 await _config .clear_all_users ()
746+ _bot_ref .dispatch ("red_bank_wipe" , - 1 )
728747 else :
729748 await _config .clear_all_members ()
749+ _bot_ref .dispatch ("red_bank_wipe" , None )
730750
731751 await _config .is_global .set (global_ )
732752 _cache_is_global = global_
753+ _bot_ref .dispatch ("red_bank_set_global" , global_ )
733754 return global_
734755
735756
@@ -1106,3 +1127,69 @@ def to_dict(self) -> dict:
11061127
11071128 def to_json (self ) -> str :
11081129 return json .dumps (self .to_dict ())
1130+
1131+
1132+ class BankSetBalanceInformation (NamedTuple ):
1133+ recipient : Union [discord .Member , discord .User ]
1134+ guild : Union [discord .Guild , None ]
1135+ recipient_old_balance : int
1136+ recipient_new_balance : int
1137+
1138+ def to_dict (self ) -> dict :
1139+ return {
1140+ "recipient" : self .recipient .id ,
1141+ "guild" : getattr (self .guild , "id" , None ),
1142+ "recipient_old_balance" : self .recipient_old_balance ,
1143+ "recipient_new_balance" : self .recipient_new_balance ,
1144+ }
1145+
1146+ def to_json (self ) -> str :
1147+ return json .dumps (self .to_dict ())
1148+
1149+
1150+ class BankTransferInformation (NamedTuple ):
1151+ sender : Union [discord .Member , discord .User ]
1152+ recipient : Union [discord .Member , discord .User ]
1153+ guild : Union [discord .Guild , None ]
1154+ transfer_amount : int
1155+ sender_new_balance : int
1156+ recipient_new_balance : int
1157+
1158+ def to_dict (self ) -> dict :
1159+ return {
1160+ "sender" : self .sender .id ,
1161+ "recipient" : self .recipient .id ,
1162+ "guild" : getattr (self .guild , "id" , None ),
1163+ "transfer_amount" : self .transfer_amount ,
1164+ "sender_new_balance" : self .sender_new_balance ,
1165+ "recipient_new_balance" : self .recipient_new_balance ,
1166+ }
1167+
1168+ def to_json (self ) -> str :
1169+ return json .dumps (self .to_dict ())
1170+
1171+
1172+ class BankPruneInformation (NamedTuple ):
1173+ guild : Union [discord .Guild , None ]
1174+ user_id : Union [int , None ]
1175+ # {user_id: {name: str, balance: int, created_at: int}}
1176+ pruned_users : Dict [str , Dict [str , Union [int , str ]]]
1177+
1178+ @property
1179+ def scope (self ) -> str :
1180+ if self .guild is None and self .user_id is None :
1181+ return "global"
1182+ elif self .guild is not None and self .user_id is None :
1183+ return "guild"
1184+ return "user"
1185+
1186+ def to_dict (self ) -> dict :
1187+ return {
1188+ "guild" : getattr (self .guild , "id" , None ),
1189+ "user_id" : self .user_id ,
1190+ "scope" : self .scope ,
1191+ "pruned_users" : self .pruned_users ,
1192+ }
1193+
1194+ def to_json (self ) -> str :
1195+ return json .dumps (self .to_dict ())
0 commit comments