Skip to content

Commit e07b27f

Browse files
committed
include PR Cog-Creators#5325 from Yami
1 parent 07cd270 commit e07b27f

File tree

4 files changed

+168
-30
lines changed

4 files changed

+168
-30
lines changed

docs/framework_events.rst

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,54 @@ Economy
2626
:param payload.new_balance: The new balance of the user after the payday claim. (:class:`int`)
2727
:method payload.to_dict: Returns a serializable dictionary representation of the payload. (:class:`dict`)
2828
:method payload.to_json: Returns the payload as JSON. (:class:`str`)
29+
30+
31+
Bank
32+
^^^^
33+
34+
.. py:method:: red_bank_set_balance(payload: redbot.core.bank.BankSetBalanceInformation)
35+
36+
Dispatched when a user's balance is changed.
37+
38+
:type payload: redbot.core.bank.BankSetBalanceInformation
39+
:param payload.recipient: The member whose balance is being set. (:class:`discord.Member`, :class:`discord.User`)
40+
:param payload.guild: The guild where the balance is being set. (:class:`discord.Guild`, :class:`NoneType`)
41+
:param payload.recipient_old_balance: The old balance of the user before the change. (:class:`int`)
42+
:param payload.recipient_new_balance: The new balance of the user after the change. (:class:`int`)
43+
:method payload.to_dict: Returns a serializable dictionary representation of the payload. (:class:`dict`)
44+
:method payload.to_json: Returns the payload as JSON. (:class:`str`)
45+
46+
.. py:method:: red_bank_transfer_credits(payload: redbot.core.bank.BankTransferInformation)
47+
48+
Dispatched when a user transfers currency to another user.
49+
50+
:type payload: redbot.core.bank.BankTransferInformation
51+
:param payload.sender: The member who is sending currency. (:class:`discord.Member`, :class:`discord.User`)
52+
:param payload.recipient: The member who is receiving currency. (:class:`discord.Member`, :class:`discord.User`)
53+
:param payload.guild: The guild where the transfer is taking place. (:class:`discord.Guild`, :class:`NoneType`)
54+
:param payload.transfer_amount: The amount of currency being transferred. (:class:`int`)
55+
:param payload.sender_new_balance: The new balance of the sender after the transfer. (:class:`int`)
56+
:param payload.recipient_new_balance: The new balance of the recipient after the transfer. (:class:`int`)
57+
:method payload.to_dict: Returns a serializable dictionary representation of the payload. (:class:`dict`)
58+
:method payload.to_json: Returns the payload as JSON. (:class:`str`)
59+
60+
61+
.. py:method:: red_bank_prune(payload: redbot.core.bank.BankPruneInformation)
62+
63+
Dispatched when a user is pruned from the bank.
64+
65+
:type payload: redbot.core.bank.BankPruneInformation
66+
:param payload.guild: The guild where the user is being pruned. (:class:`discord.Guild`, :class:`NoneType`)
67+
:param payload.user_id: The ID of the user being pruned. (:class:`int`, :class:`NoneType`)
68+
:param payload.pruned_users: Dict of pruned user accounts {user_id: {name: str, balance: int, created_at: int}}. (:class:`dict`)
69+
:method payload.to_dict: Returns a serializable dictionary representation of the payload. (:class:`dict`)
70+
:method payload.to_json: Returns the payload as JSON. (:class:`str`)
71+
72+
73+
.. py:method:: red_bank_wipe(guild_id: int)
74+
75+
Dispatched when a guild's bank is wiped. :code:`guild_id` will be the ID of the Guild that was wiped, -1 if all users were wiped (global bank), or None if all Guilds were wiped (local bank).
76+
77+
.. py:method:: red_bank_set_global(global_state: bool)
78+
79+
Dispatched when the global bank is enabled or disabled. :code:`global_state` will be True if the Bank is being set to Global or False if the bank is being set to Local

redbot/core/bank.py

Lines changed: 114 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import asyncio
44
import logging
55
from 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
77
from functools import wraps
88
import json
99

@@ -47,6 +47,10 @@
4747
"set_default_balance",
4848
"AbortPurchase",
4949
"cost",
50+
"PaydayClaimInformation",
51+
"BankSetBalanceInformation",
52+
"BankTransferInformation",
53+
"BankPruneInformation",
5054
)
5155

5256
_MAX_BALANCE = 2**63 - 1
@@ -82,9 +86,13 @@
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

491504
async 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

507522
async 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

568586
async 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())

redbot/core/bot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,7 +1154,7 @@ async def _pre_connect(self) -> None:
11541154
await self.add_cog(Dev())
11551155

11561156
await modlog._init(self)
1157-
await bank._init()
1157+
await bank._init(self)
11581158

11591159
packages = OrderedDict()
11601160

redbot/pytest/economy.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66

77
@pytest.fixture()
8-
async def bank(config, monkeypatch):
8+
async def bank(config, monkeypatch, red):
99
from redbot.core import Config
1010

1111
with monkeypatch.context() as m:
1212
m.setattr(Config, "get_conf", lambda *args, **kwargs: config)
1313
# noinspection PyProtectedMember
14-
await bank_module._init()
14+
await bank_module._init(red)
1515
return bank_module

0 commit comments

Comments
 (0)