2020-05-28 01:30:55 +02:00
|
|
|
import re
|
2020-08-30 00:32:32 +02:00
|
|
|
from datetime import datetime, timedelta, time
|
2020-05-28 15:14:18 +02:00
|
|
|
from operator import xor
|
2020-05-28 10:32:30 +02:00
|
|
|
|
|
|
|
import ics
|
2020-05-28 01:30:55 +02:00
|
|
|
import requests
|
2020-05-28 14:32:34 +02:00
|
|
|
from discord import Embed, DMChannel, TextChannel
|
2021-02-08 15:12:10 +01:00
|
|
|
from discord.abc import GuildChannel
|
2020-05-28 01:30:55 +02:00
|
|
|
from discord.ext import commands
|
2020-05-28 10:32:30 +02:00
|
|
|
from discord.ext import tasks
|
2021-02-08 15:12:10 +01:00
|
|
|
from discord.ext.commands import BadArgument, MissingPermissions
|
|
|
|
from discord_slash import cog_ext, SlashCommandOptionType, SlashContext
|
|
|
|
from discord_slash.utils import manage_commands
|
2020-05-28 01:30:55 +02:00
|
|
|
|
2021-02-08 15:12:10 +01:00
|
|
|
from administrator import db, slash
|
|
|
|
from administrator.check import guild_only, has_permissions, is_enabled
|
|
|
|
from administrator.logger import logger
|
2020-05-28 01:30:55 +02:00
|
|
|
|
2020-05-28 15:55:37 +02:00
|
|
|
|
2020-05-28 01:30:55 +02:00
|
|
|
extension_name = "calendar"
|
|
|
|
logger = logger.getChild(extension_name)
|
|
|
|
url_re = re.compile(r"http:\/\/adelb\.univ-lyon1\.fr\/jsp\/custom\/modules\/plannings\/anonymous_cal\.jsp\?resources="
|
|
|
|
r"([0-9]+)&projectId=([0-9]+)")
|
|
|
|
|
|
|
|
|
2020-05-28 12:13:41 +02:00
|
|
|
def query_calendar(name: str, guild: int) -> db.Calendar:
|
|
|
|
s = db.Session()
|
|
|
|
c: db.Calendar = s.query(db.Calendar).filter(db.Calendar.server == guild).filter(db.Calendar.name == name).first()
|
|
|
|
s.close()
|
|
|
|
if not c:
|
|
|
|
raise BadArgument()
|
|
|
|
return c
|
|
|
|
|
|
|
|
|
2020-05-28 01:30:55 +02:00
|
|
|
class Calendar(commands.Cog):
|
|
|
|
def __init__(self, bot: commands.Bot):
|
|
|
|
self.bot = bot
|
2021-02-08 15:12:10 +01:00
|
|
|
slash.get_cog_commands(self)
|
|
|
|
|
|
|
|
@cog_ext.cog_subcommand(base="calendar", name="define", description="Define a calendar", options=[
|
|
|
|
manage_commands.create_option("name", "The name of the calendar", SlashCommandOptionType.STRING, True),
|
|
|
|
manage_commands.create_option("url", "The url of the calendar", SlashCommandOptionType.STRING, True)
|
|
|
|
])
|
|
|
|
@is_enabled()
|
|
|
|
@guild_only()
|
|
|
|
@has_permissions(manage_channels=True)
|
|
|
|
async def calendar_define(self, ctx: SlashContext, name: str, url: str):
|
2020-05-28 01:30:55 +02:00
|
|
|
try:
|
|
|
|
ics.Calendar(requests.get(url).text)
|
|
|
|
except Exception:
|
|
|
|
raise BadArgument()
|
|
|
|
m = url_re.findall(url)
|
|
|
|
if not m:
|
|
|
|
raise BadArgument()
|
|
|
|
|
|
|
|
s = db.Session()
|
2020-05-28 10:32:30 +02:00
|
|
|
if s.query(db.Calendar).filter(db.Calendar.server == ctx.guild.id).filter(db.Calendar.name == name).first():
|
2020-05-28 01:30:55 +02:00
|
|
|
s.close()
|
|
|
|
raise BadArgument()
|
2020-05-28 10:32:30 +02:00
|
|
|
s.add(db.Calendar(name, int(m[0][0]), int(m[0][1]), ctx.guild.id))
|
2020-05-28 01:30:55 +02:00
|
|
|
s.commit()
|
|
|
|
s.close()
|
2021-02-08 15:12:10 +01:00
|
|
|
await ctx.send(content="\U0001f44d")
|
2020-05-28 01:30:55 +02:00
|
|
|
|
2021-02-08 15:12:10 +01:00
|
|
|
@cog_ext.cog_subcommand(base="calendar", name="list", description="List all server calendar")
|
|
|
|
@is_enabled()
|
|
|
|
@guild_only()
|
|
|
|
async def calendar_list(self, ctx: SlashContext):
|
2020-05-28 01:30:55 +02:00
|
|
|
embed = Embed(title="Calendar list")
|
|
|
|
s = db.Session()
|
|
|
|
for c in s.query(db.Calendar).filter(db.Calendar.server == ctx.guild.id).all():
|
|
|
|
embed.add_field(name=c.name, value=f"resources: {c.resources} | project id: {c.project_id}", inline=False)
|
|
|
|
s.close()
|
2021-02-08 15:12:10 +01:00
|
|
|
await ctx.send(embeds=[embed])
|
|
|
|
|
|
|
|
@cog_ext.cog_subcommand(base="calendar", name="remove", description="Remove a server calendar", options=[
|
|
|
|
manage_commands.create_option("name", "The name of the calendar", SlashCommandOptionType.STRING, True)
|
|
|
|
])
|
|
|
|
@guild_only()
|
|
|
|
@has_permissions(manage_channels=True)
|
|
|
|
async def calendar_remove(self, ctx: SlashContext, name: str):
|
|
|
|
s = db.Session()
|
|
|
|
c = s.query(db.Calendar).filter(db.Calendar.server == ctx.guild.id).filter(db.Calendar.name == name).first()
|
|
|
|
if c:
|
|
|
|
s.delete(c)
|
|
|
|
s.commit()
|
|
|
|
s.close()
|
|
|
|
await ctx.send(content="\U0001f44d")
|
2020-05-28 01:30:55 +02:00
|
|
|
else:
|
2021-02-08 15:12:10 +01:00
|
|
|
s.close()
|
|
|
|
raise BadArgument()
|
2020-05-28 01:30:55 +02:00
|
|
|
|
2021-02-08 15:12:10 +01:00
|
|
|
@cog_ext.cog_subcommand(base="calendar", name="day", description="Show the current day or the given day", options=[
|
|
|
|
manage_commands.create_option("name", "The name of the calendar", SlashCommandOptionType.STRING, True),
|
|
|
|
manage_commands.create_option("date", "A target date", SlashCommandOptionType.STRING, False)
|
|
|
|
])
|
|
|
|
@is_enabled()
|
|
|
|
@guild_only()
|
|
|
|
async def calendar_day(self, ctx: SlashContext, name: str, day: str = None):
|
2020-05-28 12:13:41 +02:00
|
|
|
c = query_calendar(name, ctx.guild.id)
|
2020-05-28 10:32:30 +02:00
|
|
|
if day is None:
|
2020-05-28 16:52:23 +02:00
|
|
|
date = datetime.now().date()
|
2020-05-28 10:32:30 +02:00
|
|
|
else:
|
|
|
|
try:
|
2020-05-28 16:52:23 +02:00
|
|
|
date = datetime.strptime(day, "%d/%m/%Y").date()
|
2020-05-28 10:32:30 +02:00
|
|
|
except ValueError:
|
|
|
|
raise BadArgument()
|
2020-08-30 18:13:39 +02:00
|
|
|
|
|
|
|
embed = c.day_embed(date)
|
|
|
|
|
2020-05-28 16:52:23 +02:00
|
|
|
s = db.Session()
|
|
|
|
if s.is_modified(c):
|
|
|
|
s.add(c)
|
|
|
|
s.commit()
|
|
|
|
s.close()
|
2021-02-08 15:12:10 +01:00
|
|
|
await ctx.send(embeds=[embed])
|
|
|
|
|
|
|
|
@cog_ext.cog_subcommand(base="calendar", name="week", description="Show the week or the given week", options=[
|
|
|
|
manage_commands.create_option("name", "The name of the calendar", SlashCommandOptionType.STRING, True),
|
|
|
|
manage_commands.create_option("date", "A target date", SlashCommandOptionType.STRING, False)
|
|
|
|
])
|
|
|
|
@is_enabled()
|
|
|
|
@guild_only()
|
|
|
|
async def calendar_week(self, ctx: SlashContext, name: str, day: str = None):
|
2020-05-28 12:13:41 +02:00
|
|
|
c = query_calendar(name, ctx.guild.id)
|
|
|
|
if day is None:
|
2020-05-28 16:52:23 +02:00
|
|
|
date = datetime.now().date()
|
2020-05-28 12:13:41 +02:00
|
|
|
else:
|
|
|
|
try:
|
2020-05-28 16:52:23 +02:00
|
|
|
date = datetime.strptime(day, "%d/%m/%Y").date()
|
2020-05-28 12:13:41 +02:00
|
|
|
except ValueError:
|
|
|
|
raise BadArgument()
|
2020-08-30 00:32:32 +02:00
|
|
|
|
|
|
|
embed = c.week_embed(date)
|
|
|
|
|
2020-05-28 16:52:23 +02:00
|
|
|
s = db.Session()
|
|
|
|
if s.is_modified(c):
|
|
|
|
s.add(c)
|
|
|
|
s.commit()
|
|
|
|
s.close()
|
2021-02-08 15:12:10 +01:00
|
|
|
await ctx.send(embeds=[embed])
|
|
|
|
|
|
|
|
@cog_ext.cog_subcommand(base="calendar", subcommand_group="notify", name="add",
|
|
|
|
description="Notify you or the giver channel of calendar events",
|
|
|
|
options=[
|
|
|
|
manage_commands.create_option("name", "The name of the calendar",
|
|
|
|
SlashCommandOptionType.STRING, True),
|
|
|
|
manage_commands.create_option("channel", "A target channel",
|
|
|
|
SlashCommandOptionType.CHANNEL, False)
|
|
|
|
])
|
|
|
|
@is_enabled()
|
|
|
|
@guild_only()
|
|
|
|
async def calendar_notify_set(self, ctx: SlashContext, name: str, channel: GuildChannel = None):
|
|
|
|
await self.add_remove_calendar(ctx, name, channel, True)
|
|
|
|
|
|
|
|
@cog_ext.cog_subcommand(base="calendar", subcommand_group="notify", name="remove",
|
|
|
|
description="Remove the calendar notify of the current user or the given channel",
|
|
|
|
options=[
|
|
|
|
manage_commands.create_option("name", "The name of the calendar",
|
|
|
|
SlashCommandOptionType.STRING, True),
|
|
|
|
manage_commands.create_option("channel", "A target channel",
|
|
|
|
SlashCommandOptionType.CHANNEL, False)
|
|
|
|
])
|
|
|
|
@is_enabled()
|
|
|
|
@guild_only()
|
|
|
|
async def calendar_notify_remove(self, ctx: SlashContext, name: str, channel: GuildChannel = None):
|
|
|
|
await self.add_remove_calendar(ctx, name, channel, False)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
async def add_remove_calendar(ctx: SlashContext, name: str, channel: GuildChannel, action: bool):
|
|
|
|
if channel:
|
|
|
|
if not isinstance(channel, TextChannel):
|
|
|
|
raise BadArgument()
|
|
|
|
if not channel.permissions_for(ctx.author).manage_channels:
|
|
|
|
raise MissingPermissions(["manage_channels"])
|
|
|
|
else:
|
|
|
|
m = channel.id
|
2020-05-28 14:38:13 +02:00
|
|
|
else:
|
2021-02-08 15:12:10 +01:00
|
|
|
if not ctx.author.dm_channel:
|
|
|
|
await ctx.author.create_dm()
|
|
|
|
m = ctx.author.dm_channel.id
|
2020-05-28 14:38:13 +02:00
|
|
|
|
|
|
|
s = db.Session()
|
|
|
|
c = query_calendar(name, ctx.guild.id)
|
|
|
|
n = s.query(db.CalendarNotify).filter(db.CalendarNotify.channel == m) \
|
|
|
|
.filter(db.CalendarNotify.calendar_id == c.id) \
|
|
|
|
.first()
|
2021-02-08 15:12:10 +01:00
|
|
|
|
|
|
|
if action and not n:
|
|
|
|
s.add(db.CalendarNotify(m, c.id))
|
|
|
|
elif not action and n:
|
2020-05-28 14:45:31 +02:00
|
|
|
s.delete(n)
|
2020-05-28 14:16:52 +02:00
|
|
|
else:
|
|
|
|
s.close()
|
|
|
|
raise BadArgument()
|
2020-05-28 13:58:45 +02:00
|
|
|
s.commit()
|
|
|
|
s.close()
|
|
|
|
|
2021-02-08 15:12:10 +01:00
|
|
|
await ctx.send(content="\U0001f44d")
|
|
|
|
|
|
|
|
@cog_ext.cog_subcommand(base="calendar", subcommand_group="notify", name="list",
|
|
|
|
description="List all notify of all calendar or the given one",
|
|
|
|
options=[
|
|
|
|
manage_commands.create_option("name", "The name of the calendar",
|
|
|
|
SlashCommandOptionType.STRING, True)
|
|
|
|
])
|
|
|
|
@is_enabled()
|
|
|
|
@guild_only()
|
|
|
|
@has_permissions(manage_channels=True)
|
|
|
|
async def calendar_notify_list(self, ctx: SlashContext, name: str = None):
|
2020-05-28 14:32:34 +02:00
|
|
|
s = db.Session()
|
|
|
|
embed = Embed(title="Notify list")
|
2020-05-28 14:45:31 +02:00
|
|
|
if name is None:
|
|
|
|
calendars = s.query(db.Calendar).filter(db.Calendar.server == ctx.guild.id).all()
|
|
|
|
else:
|
|
|
|
calendars = [query_calendar(name, ctx.guild.id)]
|
|
|
|
for c in calendars:
|
2020-05-28 14:32:34 +02:00
|
|
|
notify = []
|
|
|
|
for n in c.calendars_notify:
|
|
|
|
ch = self.bot.get_channel(n.channel)
|
|
|
|
if type(ch) == TextChannel:
|
|
|
|
notify.append(ch.mention)
|
|
|
|
elif type(ch) == DMChannel:
|
|
|
|
notify.append(ch.recipient.mention)
|
2020-05-28 14:45:31 +02:00
|
|
|
embed.add_field(name=c.name, value="\n".join(notify) or "Nothing here", inline=False)
|
2021-02-08 15:12:10 +01:00
|
|
|
await ctx.send(embeds=[embed])
|
2020-05-28 14:32:34 +02:00
|
|
|
|
2020-05-28 15:14:18 +02:00
|
|
|
@tasks.loop(minutes=1)
|
|
|
|
async def calendar_notify_loop(self):
|
|
|
|
s = db.Session()
|
2020-08-29 23:48:16 +02:00
|
|
|
now = datetime.now().astimezone(tz=None)
|
2020-08-30 18:13:39 +02:00
|
|
|
|
2020-05-28 15:14:18 +02:00
|
|
|
for c in s.query(db.Calendar).all():
|
2020-08-30 18:13:39 +02:00
|
|
|
if now.time() >= time(hour=20) and c.last_notify.astimezone(tz=None) < now.replace(hour=20, minute=00) and\
|
|
|
|
now.isoweekday() not in [5, 6]:
|
|
|
|
c.last_notify = now
|
|
|
|
s.add(c)
|
|
|
|
s.commit()
|
|
|
|
|
2020-08-30 00:32:32 +02:00
|
|
|
for n in c.calendars_notify:
|
2020-08-30 18:13:39 +02:00
|
|
|
if now.isoweekday() == 7:
|
|
|
|
await n.next_week_resume(self.bot)
|
|
|
|
else:
|
|
|
|
await n.next_day_resume(self.bot)
|
2020-08-30 00:32:32 +02:00
|
|
|
|
2020-05-28 16:52:23 +02:00
|
|
|
for e in c.events(now.date(), now.date()):
|
2020-08-29 23:48:16 +02:00
|
|
|
if xor(c.last_notify.astimezone(tz=None) < e.begin - timedelta(minutes=30) <= now,
|
|
|
|
c.last_notify.astimezone(tz=None) < e.begin - timedelta(minutes=10) <= now):
|
2020-08-30 18:13:39 +02:00
|
|
|
c.last_notify = now
|
|
|
|
s.add(c)
|
|
|
|
s.commit()
|
|
|
|
|
2020-08-30 00:32:32 +02:00
|
|
|
for n in c.calendars_notify:
|
|
|
|
await n.notify(self.bot, e)
|
2020-08-30 18:13:39 +02:00
|
|
|
|
2020-08-30 00:32:32 +02:00
|
|
|
break
|
2020-05-28 16:52:23 +02:00
|
|
|
s.close()
|
2020-05-28 15:14:18 +02:00
|
|
|
|
|
|
|
def cog_unload(self):
|
|
|
|
self.calendar_notify_loop.stop()
|
|
|
|
|
2020-05-28 01:30:55 +02:00
|
|
|
|
|
|
|
def setup(bot):
|
|
|
|
logger.info(f"Loading...")
|
|
|
|
try:
|
2020-05-28 15:14:18 +02:00
|
|
|
calendar = Calendar(bot)
|
|
|
|
bot.add_cog(calendar)
|
|
|
|
calendar.calendar_notify_loop.start()
|
2020-05-28 01:30:55 +02:00
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"Error loading: {e}")
|
|
|
|
else:
|
|
|
|
logger.info(f"Load successful")
|
|
|
|
|
|
|
|
|
|
|
|
def teardown(bot):
|
|
|
|
logger.info(f"Unloading...")
|
|
|
|
try:
|
|
|
|
bot.remove_cog("Calendar")
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"Error unloading: {e}")
|
|
|
|
else:
|
|
|
|
logger.info(f"Unload successful")
|