Add /remind list, add, modify, remove

This commit is contained in:
2021-04-27 03:57:50 +02:00
parent 7f63fa0bfb
commit 2b3f62639b

232
main.py
View File

@ -4,46 +4,204 @@ import os
import dateparser import dateparser
import discord import discord
from discord_slash.model import SlashCommandOptionType
import pytz import pytz
from apscheduler.jobstores.base import JobLookupError
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.schedulers.asyncio import AsyncIOScheduler
from discord.ext import commands from discord.ext import commands
from discord_slash import SlashCommand from discord_slash import SlashCommand, SlashContext
from discord_slash.model import SlashCommandOptionType
from discord_slash.utils.manage_commands import create_option from discord_slash.utils.manage_commands import create_option
from dotenv import load_dotenv from dotenv import load_dotenv
bot = commands.Bot( bot = commands.Bot(
command_prefix="!", command_prefix="!",
description="Reminder bot for Discord by TheLovinator#9276", description="Reminder bot for Discord by TheLovinator#9276",
intents=discord.Intents.all(), intents=discord.Intents.all(), # TODO: Find the actual intents we need.
# https://discordpy.readthedocs.io/en/latest/api.html#discord.Intents
) )
slash = SlashCommand(bot, sync_commands=True) slash = SlashCommand(bot, sync_commands=True)
def countdown(remind_id: str) -> str:
job = scheduler.get_job(remind_id)
# Get_job() returns None when it can't find a job with that id.
if job is None:
print(f"No reminder with that ID ({remind_id}).")
return "0 days, 0 hours, 0 minutes"
# Get time and date the job will run and calculate how many days, hours and seconds.
trigger_time = job.trigger.run_date
countdown = trigger_time - datetime.datetime.now(tz=pytz.timezone(config_timezone))
days, hours, minutes = (
countdown.days,
countdown.seconds // 3600,
countdown.seconds // 60 % 60,
)
the_final_countdown = ", ".join(
f"{x} {y}{'s'*(x!=1)}"
for x, y in (
(days, "day"),
(hours, "hour"),
(minutes, "minute"),
)
if x
)
return the_final_countdown
@bot.event @bot.event
async def on_error(event): async def on_error(event, *args, **kwargs):
logging.error(f"{event}") logging.error(f"{event}")
@bot.event
async def on_slash_command_error(ctx, ex):
logging.error(f"{ex}")
@bot.event @bot.event
async def on_command_error(ctx, error): async def on_command_error(ctx, error):
if isinstance(error, commands.CommandNotFound): if isinstance(error, commands.CommandNotFound):
print(error)
return return
await ctx.send(error) await ctx.send(error)
@bot.event @bot.event
async def on_ready(): async def on_ready():
logging.info(f"Logged in as {bot.user.name} ({bot.user.id})") logging.info(f"Logged in as {bot.user.name}")
@slash.slash( @slash.subcommand(
name="reminders", base="remind",
name="modify",
description="Modify a reminder.",
options=[
create_option(
name="remind_id",
description="ID for reminder we should modify. ID can be found with /remind list",
option_type=SlashCommandOptionType.STRING,
required=True,
),
create_option(
name="new_message_reason",
description="New message.",
option_type=SlashCommandOptionType.STRING,
required=False,
),
create_option(
name="new_message_date",
description="New date.",
option_type=SlashCommandOptionType.STRING,
required=False,
),
],
)
async def remind_modify(
ctx: SlashContext, remind_id: str, new_message_reason=None, new_message_date=None
):
job = scheduler.get_job(remind_id)
# Get_job() returns None when it can't find a job with that id.
if job is None:
await ctx.send(f"No reminder with that ID ({remind_id}).")
return
message = job.kwargs.get("message")
the_final_countdown_old = countdown(job.id)
channel_name = bot.get_channel(int(job.kwargs.get("channel_id")))
msg = f"**Modified** {remind_id} in #{channel_name}\n"
if new_message_reason:
try:
scheduler.modify_job(
remind_id,
kwargs={
"channel_id": job.kwargs.get("channel_id"),
"message": new_message_reason,
"author_id": job.kwargs.get("author_id"),
},
)
except JobLookupError as e:
await ctx.send(e)
msg += f"**Old message**: {message}\n**New message**: {new_message_reason}\n"
if new_message_date:
parsed_date = dateparser.parse(
f"{new_message_date}",
settings={
"PREFER_DATES_FROM": "future",
"TO_TIMEZONE": f"{config_timezone}",
},
)
remove_timezone_from_date = parsed_date.strftime("%Y-%m-%d %H:%M:%S")
try:
job = scheduler.reschedule_job(
remind_id, run_date=remove_timezone_from_date
)
except JobLookupError:
await ctx.send(f"No job by the id of {remind_id} was found")
remove_timezone_from_date_old = job.trigger.run_date.strftime("%Y-%m-%d %H:%M")
the_final_countdown_new = countdown(remind_id)
msg += (
f"**Old date**: {remove_timezone_from_date_old} (in {the_final_countdown_old})\n"
f"**New date**: {remove_timezone_from_date} (in {the_final_countdown_new})"
)
await ctx.send(msg)
@slash.subcommand(
base="remind",
name="remove",
description="Remove a reminder.",
options=[
create_option(
name="remind_id",
description="ID for reminder we should remove. ID can be found with /remind list",
option_type=SlashCommandOptionType.STRING,
required=True,
),
],
)
async def remind_remove(ctx: SlashContext, remind_id: str):
job = scheduler.get_job(remind_id)
if job is None:
await ctx.send(f"No reminder with that ID ({remind_id}).")
return
channel_id = job.kwargs.get("channel_id")
channel_name = bot.get_channel(int(channel_id))
message = job.kwargs.get("message")
trigger_time = job.trigger.run_date
try:
scheduler.remove_job(remind_id)
except JobLookupError as e:
await ctx.send(e)
await ctx.send(
(
f"**Removed** {remind_id}.\n"
f"**Time**: {trigger_time.strftime('%Y-%m-%d %H:%M')} (in {countdown(remind_id)})\n"
f"**Channel**: #{channel_name}\n"
f"**Message**: {message}"
)
)
@slash.subcommand(
base="remind",
name="list",
description="Show reminders.", description="Show reminders.",
) )
async def do_reminders(ctx): async def remind_list(ctx: SlashContext):
# We use a embed to list the reminders.
embed = discord.Embed( embed = discord.Embed(
colour=discord.Colour.random(), colour=discord.Colour.random(),
title="discord-reminder-bot by TheLovinator#9276", title="discord-reminder-bot by TheLovinator#9276",
@ -54,36 +212,20 @@ async def do_reminders(ctx):
for job in jobs: for job in jobs:
channel_id = job.kwargs.get("channel_id") channel_id = job.kwargs.get("channel_id")
channel_name = bot.get_channel(int(channel_id)) channel_name = bot.get_channel(int(channel_id))
# Only add reminders from channels in server we run "/reminder list" in
for channel in ctx.guild.channels: for channel in ctx.guild.channels:
if channel.id == channel_id: if channel.id == channel_id:
message = job.kwargs.get("message") message = job.kwargs.get("message")
trigger_time = job.trigger.run_date trigger_time = job.trigger.run_date
countdown = trigger_time - datetime.datetime.now(
tz=pytz.timezone(config_timezone)
)
days, hours, minutes = (
countdown.days,
countdown.seconds // 3600,
countdown.seconds // 60 % 60,
)
the_final_countdown = ", ".join(
f"{x} {y}{'s'*(x!=1)}"
for x, y in (
(days, "day"),
(hours, "hour"),
(minutes, "minute"),
)
if x
)
embed.add_field( embed.add_field(
name=f"{message} in #{channel_name}", name=f"{message} in #{channel_name}",
value=f"{trigger_time.strftime('%Y-%m-%d %H:%M')} (in {the_final_countdown})", value=f"{trigger_time.strftime('%Y-%m-%d %H:%M')} (in {countdown(job.id)})\nID: {job.id}",
inline=False, inline=False,
) )
# The empty embed has 76 characters
if len(embed) <= 76: if len(embed) <= 76:
msg = f"{ctx.guild.name} has no reminders." msg = f"{ctx.guild.name} has no reminders."
await ctx.send(msg) await ctx.send(msg)
@ -91,8 +233,9 @@ async def do_reminders(ctx):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@slash.slash( @slash.subcommand(
name="remind", base="remind",
name="add",
description="Set a reminder.", description="Set a reminder.",
options=[ options=[
create_option( create_option(
@ -109,9 +252,7 @@ async def do_reminders(ctx):
), ),
], ],
) )
async def do_remind(ctx, message_date: str, message_reason: str): async def remind_add(ctx: SlashContext, message_date: str, message_reason: str):
logging.info(f"New Discord message: {ctx.message}")
parsed_date = dateparser.parse( parsed_date = dateparser.parse(
f"{message_date}", f"{message_date}",
settings={ settings={
@ -119,31 +260,24 @@ async def do_remind(ctx, message_date: str, message_reason: str):
"TO_TIMEZONE": f"{config_timezone}", "TO_TIMEZONE": f"{config_timezone}",
}, },
) )
remove_timezone_from_date = parsed_date.strftime("%Y-%m-%d %H:%M:%S")
logging.debug(f"Date from command: {message_date}") run_date = parsed_date.strftime("%Y-%m-%d %H:%M:%S")
logging.debug(f"Reason from command: {message_reason}") reminder = scheduler.add_job(
logging.debug(f"Parsed date: {parsed_date}")
logging.debug(f"Date without timezone: {remove_timezone_from_date}")
logging.debug(f"Discord channel ID: {ctx.channel.id}")
logging.debug(f"Discord channel name: {ctx.channel.name}")
job = scheduler.add_job(
send_to_discord, send_to_discord,
run_date=remove_timezone_from_date, run_date=run_date,
kwargs={ kwargs={
"channel_id": ctx.channel_id, "channel_id": ctx.channel_id,
"message": message_reason, "message": message_reason,
"author_id": ctx.author_id, "author_id": ctx.author_id,
}, },
) )
logging.debug(f"Job id: '{job.id}', name: '{job.name}' and kwargs: '{job.kwargs}'")
message = ( message = (
f"Hello {ctx.author.display_name}, I will notify you at:\n" f"Hello {ctx.author.display_name}, I will notify you at:\n"
f"**{remove_timezone_from_date}**\n" f"**{run_date}** (in {countdown(reminder.id)})\n"
f"With the message:\n**{message_reason}**. " f"With the message:\n**{message_reason}**."
) )
logging.debug(f"Message we sent back to user in Discord:\n {message}")
await ctx.send(message) await ctx.send(message)