diff --git a/README.md b/README.md
index 1d666fb..7c516ae 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,8 @@
Cog Function
| Name | Status | Description (Click to see full status)
-| --- | --- | --- |
+| --- | --- | --- |
+| announcedaily | **Alpha** | Send daily announcements to all servers at a specified times
Commissioned release, so suggestions will not be accepted |
| ccrole | **Beta** | Create custom commands that also assign roles
May have some bugs, please create an issue if you find any |
| chatter | **Alpha** | Chat-bot trained to talk like your guild
Missing some key features, but currently functional |
| coglint | **Alpha** | Error check code in python syntax posted to discord
Works, but probably needs more turning to work for cogs |
diff --git a/announcedaily/__init__.py b/announcedaily/__init__.py
new file mode 100644
index 0000000..8cc69d5
--- /dev/null
+++ b/announcedaily/__init__.py
@@ -0,0 +1,9 @@
+from redbot.core.bot import Red
+
+from .announcedaily import AnnounceDaily
+
+
+def setup(bot: Red):
+ daily = AnnounceDaily(bot)
+ bot.add_cog(daily)
+ bot.loop.create_task(daily.check_day())
diff --git a/announcedaily/announcedaily.py b/announcedaily/announcedaily.py
new file mode 100644
index 0000000..17a1bd7
--- /dev/null
+++ b/announcedaily/announcedaily.py
@@ -0,0 +1,250 @@
+import asyncio
+import random
+from datetime import datetime, timedelta
+
+import discord
+from redbot.core import Config, checks, commands
+from redbot.core.bot import Red
+from redbot.core.data_manager import cog_data_path
+from redbot.core.utils.chat_formatting import pagify, box
+
+DEFAULT_MESSAGES = [
+ # "Example message. Uncomment and overwrite to use",
+ # "Example message 2. Each message is in quotes and separated by a comma"
+]
+
+
+class AnnounceDaily:
+ """
+ Send daily announcements
+ """
+
+ def __init__(self, bot: Red):
+ self.bot = bot
+ self.path = str(cog_data_path(self)).replace('\\', '/')
+
+ self.image_path = self.path + "/"
+
+ self.config = Config.get_conf(self, identifier=9811198108111121, force_registration=True)
+ default_global = {
+ 'messages': [],
+ 'images': [],
+ 'time': {'hour': 0, 'minute': 0, 'second': 0}
+ }
+ default_guild = {
+ "channelid": None
+ }
+
+ self.config.register_global(**default_global)
+ self.config.register_guild(**default_guild)
+
+ async def _get_msgs(self):
+ return DEFAULT_MESSAGES + await self.config.messages()
+
+ @commands.group(name="announcedaily", aliases=['annd'])
+ @checks.mod_or_permissions(administrator=True)
+ @commands.guild_only()
+ async def _ad(self, ctx: commands.Context):
+ """
+ Base command for managing AnnounceDaily settings
+
+ Do `[p]help annd ` for more details
+ """
+ if ctx.invoked_subcommand is None:
+ pass
+
+ @commands.command()
+ @checks.guildowner()
+ @commands.guild_only()
+ async def runannounce(self, ctx: commands.Context):
+ """Manually run the daily announcement"""
+
+ await self.send_announcements()
+ await ctx.send("Success")
+
+ @_ad.command()
+ async def setchannel(self, ctx: commands.Context, channel: discord.TextChannel = None):
+ """
+ Set the announcement channel for this server
+
+ Don't pass a channel to clear this server of receiving announcements
+ """
+ if channel is not None:
+ await self.config.guild(ctx.guild).channelid.set(channel.id)
+ await ctx.send("Announcement channel has been set to {}".format(channel.mention))
+ else:
+ await self.config.guild(ctx.guild).channelid.set(None)
+ await ctx.send("Announcement channel has been cleared")
+
+ @_ad.command()
+ async def addmsg(self, ctx: commands.Context, *, msg):
+ """
+ Add a message to the pool of announcement messages
+ """
+ async with self.config.messages() as msgs:
+ msgs.append(msg)
+
+ await ctx.send("Message successfully added!")
+
+ @_ad.command()
+ async def addimg(self, ctx: commands.Context, filename=None):
+ """
+ Add an image to the pool of announcement images
+
+ You must attach an image while executing this command
+ """
+ if ctx.message.attachments:
+ att_ = ctx.message.attachments[0]
+ try:
+ h = att_.height
+ except AttributeError:
+ await ctx.send("You must attach an image, no other file will be accepted")
+ return
+
+ if filename is None:
+ filename = att_.filename
+
+ try:
+ # with open(self.image_path + filename, 'w') as f:
+ # await att_.save(f)
+ await att_.save(self.image_path + filename)
+ except discord.NotFound:
+ await ctx.send("Did you delete the message? Cause I couldn't download the attachment")
+ except discord.HTTPException:
+ await ctx.send("Failed to download the attachment, please try again")
+ else:
+ async with self.config.images() as images:
+ if filename in images:
+ await ctx.send("Image {} has been overwritten!".format(filename))
+ else:
+ images.append(filename)
+ await ctx.send("Image {} has been added!".format(filename))
+ else:
+ await ctx.send("You must attach an image when sending this command")
+
+ @_ad.command()
+ async def listmsg(self, ctx: commands.Context):
+ """
+ List all registered announcement messages
+ """
+ messages = await self.config.messages()
+ for page in pagify("\n".join("{} - {}".format(key, image) for key, image in enumerate(messages))):
+ await ctx.send(box(page))
+ await ctx.send("Done!")
+
+ @_ad.command()
+ async def listimg(self, ctx: commands.Context):
+ """
+ List all registered announcement immages
+ """
+ images = await self.config.images()
+ for page in pagify("\n".join(images)):
+ await ctx.send(box(page))
+ await ctx.send("Done!")
+
+ @_ad.command()
+ async def delmsg(self, ctx: commands.Context, index: int):
+ """
+ Remove a message from the announcement pool
+
+ Must provide the index of the message, which can be found by using `[p]annd listmsg`
+ """
+ async with self.config.messages() as messages:
+ try:
+ out = messages.pop(index)
+ except IndexError:
+ await ctx.send("Invalid index, check valid indexes with `listmsg` command")
+ return
+
+ await ctx.send("The following message was removed:\n```{}```".format(out))
+
+ @_ad.command()
+ async def delimg(self, ctx: commands.Context, filename: str):
+ """
+ Remove an image from the announcement pool
+
+ Does not delete the file from the disk, so you may have to clean it up occasionally
+ """
+ async with self.config.images() as images:
+ if filename not in images:
+ await ctx.send("This file doesn't exist")
+ else:
+ images.remove(filename)
+ await ctx.send("Successfully removed {}".format(filename))
+
+ @_ad.command()
+ async def settime(self, ctx: commands.Context, minutes_from_now: int):
+ """
+ Set the daily announcement time
+
+ It will first announce at the time you provided, then it will repeat every 24 hours
+ """
+ ann_time = datetime.now() + timedelta(minutes=minutes_from_now)
+
+ h = ann_time.hour
+ m = ann_time.minute
+ s = ann_time.second
+ await self.config.time.set({'hour': h, 'minute': m, 'second': s})
+
+ await ctx.send("Announcements time has been set to {}::{}::{} every day\n"
+ "**Changes will apply after next scheduled announcement or reload**".format(h, m, s))
+
+ async def send_announcements(self):
+ messages = await self._get_msgs()
+ images = await self.config.images()
+
+ total = len(messages) + len(images)
+ if total < 1:
+ return
+
+ x = random.randint(0, total - 1)
+
+ if x >= len(messages):
+ x -= len(messages)
+ choice = images[x]
+ choice = open(self.image_path + choice, 'rb')
+ is_image = True
+ else:
+ choice = messages[x]
+ is_image = False
+
+ for guild in self.bot.guilds:
+ channel = await self.config.guild(guild).channelid()
+ if channel is None:
+ continue
+ channel = guild.get_channel(channel)
+ if channel is None:
+ continue
+
+ if is_image:
+ await channel.send(file=discord.File(choice))
+ else:
+ await channel.send(choice)
+
+ async def check_day(self):
+ while self is self.bot.get_cog("AnnounceDaily"):
+ tomorrow = datetime.now() + timedelta(days=1)
+ time = await self.config.time()
+ h, m, s = time['hour'], time['minute'], time['second']
+ midnight = datetime(year=tomorrow.year, month=tomorrow.month,
+ day=tomorrow.day, hour=h, minute=m, second=s)
+
+ print("Sleeping for {} seconds".format((midnight - datetime.now()).seconds))
+ await asyncio.sleep((midnight - datetime.now()).seconds)
+
+ if self is not self.bot.get_cog("AnnounceDaily"):
+ print("Announce canceled, cog has been lost")
+ return
+
+ await self.send_announcements()
+
+ await asyncio.sleep(3)
+
+# [p]setchannel #channelname - Set the announcement channel per server
+# [p]addmsg - Adds a msg to the pool
+# [p]addimg http://imgurl.com/image.jpg - Adds an image to the pool
+# [p]listmsg - Lists all messages in the pool
+# [p]listimg - Unsure about this one, but would probably just post all the images
+# [p]delmsg - Remove msg from pool
+# [p]delimg - Remove image from pool
+# [p]settime - S
diff --git a/announcedaily/info..json b/announcedaily/info..json
new file mode 100644
index 0000000..c2e9ce6
--- /dev/null
+++ b/announcedaily/info..json
@@ -0,0 +1,18 @@
+{
+ "author": [
+ "Bobloy"
+ ],
+ "bot_version": [
+ 3,
+ 0,
+ 0
+ ],
+ "description": "Send daily announcements to all servers at a specified times",
+ "hidden": true,
+ "install_msg": "Thank you for installing AnnounceDaily! Get started with `[p]help AnnounceDaily`",
+ "requirements": [],
+ "short": "Send daily announcements",
+ "tags": [
+ "bobloy"
+ ]
+}
\ No newline at end of file