Skip to content

Patreon #1917

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 41 commits into from
Oct 5, 2022
Merged

Patreon #1917

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
6ca77ac
Add patreon role configuration
mathstrains21 Sep 2, 2021
a44e798
Create `patreon.py` and add setup
mathstrains21 Sep 2, 2021
526ce5f
Merge branch 'python-discord:main' into patreon
mathstrains21 Sep 2, 2021
380dd5c
Add patron message when someone gains a patron role
mathstrains21 Sep 3, 2021
b38ec72
Merge branch 'patreon' of https://github.com/mathstrains21/bot into p…
mathstrains21 Sep 3, 2021
9abea85
Merge branch 'python-discord:main' into patreon
mathstrains21 Sep 25, 2021
feb1b47
Merge branch 'python-discord:main' into patreon
mathstrains21 Oct 24, 2021
b1f31da
Add patrons command
mathstrains21 Oct 26, 2021
241c27b
Add monthly patrons list
mathstrains21 Oct 27, 2021
3f32352
Add patreon command
mathstrains21 Oct 27, 2021
24c549f
Merge branch 'python-discord:main' into patreon
mathstrains21 Oct 27, 2021
0502044
Remove obvious type hints
mathstrains21 Oct 27, 2021
0b8e507
Merge branches 'patreon' and 'patreon' of https://github.com/mathstra…
mathstrains21 Oct 27, 2021
8705488
Fix fetch/get
mathstrains21 Oct 27, 2021
6cf0f6d
Rename current_supporters_monthly to current_monthly_supporters
mathstrains21 Oct 27, 2021
ccc2767
Fix f string
mathstrains21 Oct 27, 2021
68b382a
Rename some variables
mathstrains21 Oct 27, 2021
2b916ce
Adjust docstrings
mathstrains21 Oct 27, 2021
0b83f58
`commands.context` -> `commands.Context`
mathstrains21 Oct 27, 2021
bc2c8c5
embeds= -> embed=
mathstrains21 Oct 27, 2021
12308ee
Unify links
mathstrains21 Oct 27, 2021
c360365
Fix capitalisation
mathstrains21 Oct 27, 2021
02a02bf
Save API call
mathstrains21 Oct 28, 2021
95a07ba
Update patreon description
mathstrains21 Oct 28, 2021
af50cc4
Change command function docstrings
mathstrains21 Oct 28, 2021
f3aa56a
Save another API call
mathstrains21 Oct 28, 2021
051bc5c
Merge branch 'python-discord:main' into patreon
mathstrains21 Nov 14, 2021
0c52dbf
Update `patreon_info` function docstring
mathstrains21 Nov 28, 2021
0d4d6b9
Reflect empty line
mathstrains21 Nov 28, 2021
f9a4fb1
Merge branch 'python-discord:main' into patreon
mathstrains21 Nov 28, 2021
1af289d
Fix string quotes
mathstrains21 Nov 28, 2021
d470a18
Merge branch 'python-discord:main' into patreon
mathstrains21 Mar 13, 2022
b923b52
Merge branch 'main' into patreon
wookie184 Aug 19, 2022
00330f6
Refactor patreon tier logic, and address some review comments in the …
wookie184 Aug 19, 2022
3a2b576
Make get_patreon_tier function more readable
wookie184 Aug 20, 2022
7fe0d60
Only allow command in bot-commands, and change some formatting
wookie184 Sep 10, 2022
7e6e3ca
Use Member.get_role instead of adding has_role_id util
wookie184 Sep 11, 2022
e93a949
Wording changes and minor code changes
wookie184 Sep 11, 2022
228deaf
Update wording of patreon message
wookie184 Sep 17, 2022
1fbe38b
Add message when there are no patrons in a tier
wookie184 Oct 5, 2022
1784014
Merge branch 'main' into patreon
wookie184 Oct 5, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bot/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,10 @@ class Roles(metaclass=YAMLGetter):

jammers: int

patreon_tier_1: int
patreon_tier_2: int
patreon_tier_3: int


class Guild(metaclass=YAMLGetter):
section = "guild"
Expand Down
129 changes: 129 additions & 0 deletions bot/exts/info/patreon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import datetime

import arrow
import discord
from discord.ext import commands, tasks

from bot import constants
from bot.bot import Bot
from bot.constants import Channels, Guild, Roles, STAFF_PARTNERS_COMMUNITY_ROLES
from bot.decorators import in_whitelist
from bot.log import get_logger
from bot.utils.channel import get_or_fetch_channel

log = get_logger(__name__)

PATREON_INFORMATION = (
"Python Discord is a volunteer run non-profit organization, so we rely on Patreon donations to do what we do. "
"We use the money we get to offer excellent prizes for all of our events. These include t-shirts, "
"stickers, and sometimes even Raspberry Pis!\n\n"
"You can read more about how Patreon donations help us, and consider donating yourself, on our patreon page "
"[here](https://pydis.com/patreon)!"
)
NO_PATRONS_MESSAGE = "*There are currently no patrons at this tier.*"

# List of tuples containing tier number and Discord role ID.
# Ordered from highest tier to lowest.
PATREON_TIERS: list[tuple[int, int]] = [
(3, Roles.patreon_tier_3),
(2, Roles.patreon_tier_2),
(1, Roles.patreon_tier_1),
]


def get_patreon_tier(member: discord.Member) -> int:
"""
Get the patreon tier of `member`.

A patreon tier of 0 indicates the user is not a patron.
"""
for tier, role_id in PATREON_TIERS:
if member.get_role(role_id):
return tier
return 0


class Patreon(commands.Cog):
"""Cog that shows patreon supporters."""

def __init__(self, bot: Bot) -> None:
self.bot = bot

self.current_monthly_supporters.start()

@commands.Cog.listener()
async def on_member_update(self, before: discord.Member, after: discord.Member) -> None:
"""Send a message when someone receives a patreon role."""
old_patreon_tier = get_patreon_tier(before)
new_patreon_tier = get_patreon_tier(after)
Comment on lines +54 to +58
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has an inherent dependency on how the patreon bot works. If the patreon bot decided to take away and giveth the new role in two seperate api requests, this would cease to work. Or, if the patreon bot were to take the old role away and then give the new role, this would also cease to work.

This all hinges on the patreon bot making a single api request, and would lead to funky behaviour if their bot used more than one request--and I believe it does.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be an issue. Whatever order things happen there will still only be one member_update that involves the tier increasing, which is all that this checks.

The only slightly unintended case would be if a user moves down a tier (e.g. from 3 to 2), and the bot does this by removing 3 and then adding 2. In this case the message would be sent, although it still wouldn't be incorrect (Just saying they are now a tier 2 patron).

Fixing this would probably require waiting for other role changes to see if they change things, although this could be fairly complicated so I think it would be best to see how what we have currently works out.


if new_patreon_tier <= old_patreon_tier:
return

message = (
f":tada: {after.mention} just became a **tier {new_patreon_tier}** patron!\n"
"Support us on Patreon: https://pydis.com/patreon"
)
channel = await get_or_fetch_channel(Channels.meta)
await channel.send(message)

async def send_current_supporters(self, channel: discord.abc.Messageable, automatic: bool = False) -> None:
"""Send the current list of patreon supporters, sorted by tier level."""
guild = self.bot.get_guild(Guild.id)

embed_list = []
for tier, role_id in PATREON_TIERS:
role = guild.get_role(role_id)

# Filter out any members where this is not their highest tier.
patrons = [member for member in role.members if get_patreon_tier(member) == tier]
patron_names = [f"• {patron}" for patron in patrons]

embed = discord.Embed(
title=role.name,
description="\n".join(patron_names) if patron_names else NO_PATRONS_MESSAGE,
colour=role.colour
)
embed_list.append(embed)

main_embed = discord.Embed(
title="Patreon Supporters - Monthly Update" if automatic else "Patreon Supporters",
description=(
PATREON_INFORMATION +
"\n\nThank you to the users listed below who are already supporting us!"
),
)

await channel.send(embeds=(main_embed, *embed_list))

@commands.group("patreon", aliases=("patron",), invoke_without_command=True)
async def patreon_info(self, ctx: commands.Context) -> None:
"""Send information about how Python Discord uses Patreon."""
embed = discord.Embed(
title="Patreon",
description=(
PATREON_INFORMATION +
"\n\nTo see our current supporters, run " +
f"`{constants.Bot.prefix}patreon supporters` in <#{Channels.bot_commands}>"
)
)
await ctx.send(embed=embed)

@patreon_info.command("supporters", aliases=("patrons",))
@in_whitelist(channels=(Channels.bot_commands,), roles=STAFF_PARTNERS_COMMUNITY_ROLES)
async def patreon_supporters(self, ctx: commands.Context) -> None:
"""Sends the current list of patreon supporters, sorted by tier level."""
await self.send_current_supporters(ctx.channel)

@tasks.loop(time=datetime.time(hour=17))
async def current_monthly_supporters(self) -> None:
"""A loop running daily to see if it's the first of the month. If so call `self.send_current_supporters()`."""
now = arrow.utcnow()
if now.day == 1:
meta_channel = await get_or_fetch_channel(Channels.meta)
await self.send_current_supporters(meta_channel, automatic=True)


async def setup(bot: Bot) -> None:
"""Load the Patreon cog."""
await bot.add_cog(Patreon(bot))
5 changes: 5 additions & 0 deletions config-default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,11 @@ guild:
# Streaming
video: 764245844798079016

# Patreon
patreon_tier_1: 505040943800516611
patreon_tier_2: 743399725914390631
patreon_tier_3: 743400204367036520

moderation_roles:
- *ADMINS_ROLE
- *MOD_TEAM_ROLE
Expand Down