add valentines cog to send cards to members. also some touchups on other cogs

This commit is contained in:
brandons209 2021-02-13 04:09:44 -05:00
parent a25e461750
commit 0ff00cd12a
6 changed files with 286 additions and 4 deletions

View file

@ -192,6 +192,10 @@ class MoreAdmin(commands.Cog):
guild = ctx.guild guild = ctx.guild
to_purge = [] to_purge = []
# update members
_guilds = [g for g in self.bot.guilds if g.large and not (g.chunked or g.unavailable)]
await self.bot.request_offline_members(*_guilds)
for member in guild.members: for member in guild.members:
if member.id == self.bot.user.id: # don't want to purge the bot. if member.id == self.bot.user.id: # don't want to purge the bot.
continue continue
@ -499,13 +503,14 @@ class MoreAdmin(commands.Cog):
color = await ctx.embed_color() color = await ctx.embed_color()
# defines deleting a note for the user # defines deleting a note for the user
async def delete_note(ctx: commands.GuildContext, async def delete_note(
ctx: commands.GuildContext,
pages: list, pages: list,
controls: dict, controls: dict,
message: discord.Message, message: discord.Message,
page: int, page: int,
timeout: float, timeout: float,
emoji: str emoji: str,
): ):
async with self.config.member(member).notes() as notes: async with self.config.member(member).notes() as notes:
del notes[page] del notes[page]
@ -774,7 +779,7 @@ class MoreAdmin(commands.Cog):
for i, user in enumerate(to_purge): for i, user in enumerate(to_purge):
try: try:
await user.send(purge_msg) await user.send(purge_msg)
except: except discord.HTTPException:
pass pass
if check_messages: if check_messages:

View file

@ -1,5 +1,7 @@
from .suggestion import Suggestion from .suggestion import Suggestion
__red_end_user_data_statement__ = "This doesn't store any user data."
def setup(bot): def setup(bot):
bot.add_cog(Suggestion(bot)) bot.add_cog(Suggestion(bot))

View file

@ -1,7 +1,7 @@
import asyncio import asyncio
import discord import discord
from typing import Optional from typing import Optional, Literal
from discord.utils import get from discord.utils import get
from datetime import timedelta from datetime import timedelta
@ -722,3 +722,11 @@ class Suggestion(commands.Cog):
embed.add_field(name="Votes:", value=f"For: `{num_up}`, Against: `{num_down}`", inline=False) embed.add_field(name="Votes:", value=f"For: `{num_up}`, Against: `{num_down}`", inline=False)
return content, embed return content, embed
async def red_delete_data_for_user(
self,
*,
requester: Literal["discord_deleted_user", "owner", "user", "user_strict"],
user_id: int,
):
pass

View file

@ -0,0 +1,7 @@
from .valentine import Valentine_Cards
__red_end_user_data_statement__ = "This doesn't store any user data."
def setup(bot):
bot.add_cog(Valentine_Cards(bot))

10
valentinecards/info.json Normal file
View file

@ -0,0 +1,10 @@
{
"author" : ["brandons209"],
"bot_version": [3,4,4],
"install_msg" : "Happy Valentines day! Make sure to set the fallback channel.",
"short" : "Happy Valentines day!",
"description" : "Sends a custom message and 'card' to all users in a guild. Messages and cards are choosen randomly from the available options for each user. ",
"tags" : ["valentine", "massdm", "cards", "holiday"],
"hidden" : false,
"end_user_data_statement": "This cog won't store anything for a user."
}

250
valentinecards/valentine.py Normal file
View file

@ -0,0 +1,250 @@
from redbot.core import commands, checks, Config
from redbot.core.utils.chat_formatting import *
from redbot.core.utils.predicates import MessagePredicate
from typing import Literal
import asyncio, discord, random
class Valentine_Cards(commands.Cog):
"""
Distribute valentine (or any messages / pictures) to all members in a server.
Cards and messages are randomly selected for each person
"""
def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, identifier=8465846534353571, force_registration=True)
default_guild = {"cards": [], "dm_msgs": [], "fallback_channel": None}
self.config.register_guild(**default_guild)
@staticmethod
def format_list(items: list) -> list:
"""
Format a list into a numbered list for printing
Returns pages of text
"""
if not items:
return []
msg = "\n".join(f"{i+1}. {item}" for i, item in enumerate(items))
return pagify(msg)
@commands.group(name="vset")
@checks.admin_or_permissions(administrator=True)
@commands.guild_only()
async def vset(self, ctx):
"""
Manage cards and DM messages
"""
pass
@vset.command(name="fallback")
async def vset_fallback_channel(self, ctx, *, channel: discord.TextChannel = None):
"""
Set a channel as fallback when DM fails
Run command with no channel to reset channel
"""
if not channel:
current_channel = await self.config.guild(ctx.guild).fallback_channel()
if not current_channel:
await ctx.send(warning("No fallback channel set."))
elif not ctx.guild.get_channel(current_channel):
await ctx.send(
warning("Channel set but I cannot see the channel, so I will reset the fallback channel saved.")
)
await self.config.guild(ctx.guild).fallback_channel.clear()
else:
await ctx.send(f"Current fallback channel: {ctx.guild.get_channel(current_channel).mention}")
return
await self.config.guild(ctx.guild).fallback_channel.set(channel.id)
await ctx.tick()
@vset.group(name="cards")
async def vset_cards(self, ctx):
"""
Manage cards
"""
pass
@vset_cards.command(name="add")
async def vset_cards_add(self, ctx, *, url: str):
"""
Add a possible card to be sent to a user
Card should be a URL to the image
"""
async with self.config.guild(ctx.guild).cards() as cards:
cards.append(url)
await ctx.tick()
@vset_cards.command(name="list")
async def vset_cards_list(self, ctx):
"""
List the current cards
"""
cards = await self.config.guild(ctx.guild).cards()
if not cards:
await ctx.send(warning("No cards defined."))
return
pages = self.format_list(cards)
for page in pages:
await ctx.send(page)
@vset_cards.command(name="del")
async def vset_cards_del(self, ctx):
"""
Delete a card
"""
async with self.config.guild(ctx.guild).cards() as cards:
if not cards:
await ctx.send(warning("No cards defined."))
return
pages = self.format_list(cards)
for page in pages:
await ctx.send(box(page))
await ctx.send("Please choose the number of the message to delete.")
pred = MessagePredicate.contained_in([str(i + 1) for i in range(len(cards))], ctx=ctx)
try:
await self.bot.wait_for("message", check=pred, timeout=60)
except asyncio.TimeoutError:
await ctx.send("Took too long, cancelled.")
return
del cards[pred.result]
await ctx.tick()
@vset.group(name="dms")
async def vset_dms(self, ctx):
"""
Manage DM messages
"""
pass
@vset_dms.command(name="add")
async def vset_dms_add(self, ctx, *, msg: str):
"""
Add a possible DM message to be sent to a user
"""
async with self.config.guild(ctx.guild).dm_msgs() as dm_msgs:
dm_msgs.append(msg)
await ctx.tick()
@vset_dms.command(name="list")
async def vset_dms_list(self, ctx):
"""
List the current DM messages
"""
dm_msgs = await self.config.guild(ctx.guild).dm_msgs()
if not dm_msgs:
await ctx.send(warning("No DM messages defined."))
return
pages = self.format_list(dm_msgs)
for page in pages:
await ctx.send(box(page))
@vset_dms.command(name="del")
async def vset_dms_del(self, ctx):
"""
Delete a DM message
"""
async with self.config.guild(ctx.guild).dm_msgs() as dm_msgs:
if not dm_msgs:
await ctx.send(warning("No DM messages defined."))
return
pages = self.format_list(dm_msgs)
for page in pages:
await ctx.send(box(page))
await ctx.send("Please choose the number of the message to delete.")
pred = MessagePredicate.contained_in([str(i + 1) for i in range(len(dm_msgs))], ctx=ctx)
try:
await self.bot.wait_for("message", check=pred, timeout=60)
except asyncio.TimeoutError:
await ctx.send("Took too long, cancelled.")
return
del dm_msgs[pred.result]
await ctx.tick()
@commands.command(name="sendcards")
@checks.admin_or_permissions(administrator=True)
@commands.guild_only()
async def valentine_cards(self, ctx):
"""
Send out valentine cards!
Everyone in the server with DMs enabled will receive a custom message and card!
Failed DMs will be sent in the fallback channel, if available.
"""
# update members
_guilds = [g for g in self.bot.guilds if g.large and not (g.chunked or g.unavailable)]
await self.bot.request_offline_members(*_guilds)
members = [m for m in ctx.guild.members if m != ctx.guild.me]
fallback = ctx.guild.get_channel(await self.config.guild(ctx.guild).fallback_channel())
pred = MessagePredicate.yes_or_no(ctx)
if not fallback:
extra = warning("\n\nThere is no fallback channel set, you should set it before running this command.")
else:
extra = ""
await ctx.send(f"Are you sure you want to send cards to {len(members)} members?{extra}")
try:
await self.bot.wait_for("message", check=pred, timeout=60)
except asyncio.TimeoutError:
await ctx.send("Took too long, cancelled.")
return
if not pred.result:
await ctx.send("Cancelled.")
return
# lets gooooo
dm_msgs = await self.config.guild(ctx.guild).dm_msgs()
cards = await self.config.guild(ctx.guild).cards()
update_msg = await ctx.send(f"Processed `1` out of `{len(members)}` members.")
failed = 0
for i, member in enumerate(members):
msg = random.choice(dm_msgs)
card = random.choice(cards)
try:
await member.send(msg)
await asyncio.sleep(0.01)
await member.send(card)
except discord.HTTPException:
if fallback:
await fallback.send(msg)
await asyncio.sleep(0.01)
await fallback.send(f"{member.mention}\n{card}")
failed += 1
if i % 10 == 0:
await update_msg.edit(content=f"Processed `{i+1}` out of `{len(members)}` members.")
if failed:
await update_msg.edit(
content=f"DMed `{len(members) - failed}` members successfully and failed to send a DM to `{failed}` members, they were sent in the fallback channel if set."
)
else:
await update_msg.edit(content=f"DMed `{len(members)}` members successfully.")
async def red_delete_data_for_user(
self,
*,
requester: Literal["discord_deleted_user", "owner", "user", "user_strict"],
user_id: int,
):
pass