v4: Personal calendar #168
@ -5,7 +5,7 @@ from functools import wraps
|
|||||||
from os import getenv, path
|
from os import getenv, path
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from disnake import Client, Guild
|
from disnake import Asset, Client, Guild
|
||||||
from disnake.guild_scheduled_event import GuildScheduledEvent
|
from disnake.guild_scheduled_event import GuildScheduledEvent
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from ics import Calendar, ContentLine, Event
|
from ics import Calendar, ContentLine, Event
|
||||||
@ -117,17 +117,11 @@ def days_before_failure() -> int:
|
|||||||
return nextDelta.days
|
return nextDelta.days
|
||||||
|
|
||||||
|
|
||||||
def cdn_avatar_url(user_id: int, hash: str) -> str:
|
|
||||||
ext = "gif" if hash.startswith("a_") else "png"
|
|
||||||
return f"https://cdn.discordapp.com/avatars/{user_id}/{hash}.{ext}"
|
|
||||||
|
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def context_processor():
|
def context_processor():
|
||||||
return dict(
|
return dict(
|
||||||
_=i18n,
|
_=i18n,
|
||||||
client=client,
|
client=client,
|
||||||
cdn_avatar_url=cdn_avatar_url,
|
|
||||||
days_before_failure=days_before_failure(),
|
days_before_failure=days_before_failure(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -180,12 +174,10 @@ async def callback():
|
|||||||
@app.route("/guilds")
|
@app.route("/guilds")
|
||||||
@login_required
|
@login_required
|
||||||
async def guilds():
|
async def guilds():
|
||||||
guild = get_guild_by_id(request.args.get("guild"))
|
guild = request.args.get("guild")
|
||||||
|
|
||||||
if guild:
|
if guild:
|
||||||
return redirect(
|
return redirect(url_for(".subscribe", entity_id=guild))
|
||||||
url_for(".subscribe", guild_id=guild.vanity_url_code or guild.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
discord = make_session(token=session.get("oauth2_token"))
|
discord = make_session(token=session.get("oauth2_token"))
|
||||||
@ -201,27 +193,42 @@ async def guilds():
|
|||||||
common_guilds.append(bot_guild)
|
common_guilds.append(bot_guild)
|
||||||
|
|
||||||
return await render_template(
|
return await render_template(
|
||||||
"guilds.html.j2", user=user, common_guilds=common_guilds
|
"guilds.html.j2",
|
||||||
|
user=user,
|
||||||
|
avatar=Asset._from_avatar(None, user["id"], user["avatar"]),
|
||||||
|
common_guilds=common_guilds,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/subscribe/<guild_id>")
|
@app.route("/subscribe/<entity_id>")
|
||||||
@login_required
|
@login_required
|
||||||
async def subscribe(guild_id: str):
|
async def subscribe(entity_id: str):
|
||||||
guild = get_guild_by_id(guild_id)
|
guild = get_guild_by_id(entity_id)
|
||||||
if guild is None:
|
|
||||||
return redirect(url_for(".login"))
|
|
||||||
|
|
||||||
try:
|
if guild:
|
||||||
discord = make_session(token=session.get("oauth2_token"))
|
try:
|
||||||
user_guilds = discord.get(f"{API_BASE_URL}/users/@me/guilds").json()
|
discord = make_session(token=session.get("oauth2_token"))
|
||||||
except OAuth2Error:
|
user_guilds = discord.get(f"{API_BASE_URL}/users/@me/guilds").json()
|
||||||
return redirect(url_for(".login"))
|
except OAuth2Error:
|
||||||
|
return redirect(url_for(".login"))
|
||||||
|
|
||||||
if not any(str(guild.id) == user_guild["id"] for user_guild in user_guilds):
|
if not any(str(guild.id) == user_guild["id"] for user_guild in user_guilds):
|
||||||
return redirect(url_for(".login"))
|
return redirect(url_for(".login"))
|
||||||
|
|
||||||
return await render_template("subscribe.html.j2", guild=guild)
|
return await render_template(
|
||||||
|
"subscribe.html.j2",
|
||||||
|
avatar=guild.icon,
|
||||||
|
entity_id=guild.vanity_url_code or guild.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
user = await client.get_or_fetch_user(int(entity_id))
|
||||||
|
|
||||||
|
if user and str(user.id) == entity_id:
|
||||||
|
return await render_template(
|
||||||
|
"subscribe.html.j2", avatar=user.avatar, entity_id=user.id
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect(url_for(".login"))
|
||||||
|
|
||||||
|
|
||||||
def make_event(scheduled_event: GuildScheduledEvent, guild_id: int) -> Event:
|
def make_event(scheduled_event: GuildScheduledEvent, guild_id: int) -> Event:
|
||||||
@ -274,7 +281,7 @@ async def ical(entity_id: str):
|
|||||||
|
|
||||||
return calendar.serialize()
|
return calendar.serialize()
|
||||||
|
|
||||||
user = client.get_or_fetch_user(int(entity_id))
|
user = await client.get_or_fetch_user(int(entity_id))
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
calendar = Calendar()
|
calendar = Calendar()
|
||||||
@ -288,10 +295,14 @@ async def ical(entity_id: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
for guild in client.guilds:
|
for guild in client.guilds:
|
||||||
if guild.get_or_fetch_member(int(entity_id)):
|
if await guild.get_or_fetch_member(int(entity_id)):
|
||||||
for scheduled_event in guild.scheduled_events:
|
for scheduled_event in guild.scheduled_events:
|
||||||
event = make_event(scheduled_event, guild.id)
|
if user.id in [
|
||||||
calendar.events.append(event)
|
member.id
|
||||||
|
for member in await scheduled_event.fetch_users().flatten()
|
||||||
|
]:
|
||||||
|
event = make_event(scheduled_event, guild.id)
|
||||||
|
calendar.events.append(event)
|
||||||
|
|
||||||
return calendar.serialize()
|
return calendar.serialize()
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="description" content="" />
|
<meta name="description" content="" />
|
||||||
<meta name="keywords" content="" />
|
<meta name="keywords" content="" />
|
||||||
<title>{{ client.user.display_name }} - {{ _('The discord scheduled event calendar generator') }}</title>
|
<title>{{ client.user.display_name }} - {{ _("The discord scheduled event calendar generator") }}</title>
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="{{ url_for('static', filename='css/font-awesome.min.css') }}"/>
|
href="{{ url_for('static', filename='css/font-awesome.min.css') }}" />
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="{{ url_for('static', filename='css/global.css') }}"/>
|
href="{{ url_for('static', filename='css/global.css') }}" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
|
@ -3,17 +3,17 @@
|
|||||||
<div id="box">
|
<div id="box">
|
||||||
<div id="avatars">
|
<div id="avatars">
|
||||||
<img src="{{ url_for('static', filename='img/deadlink.png') }}"
|
<img src="{{ url_for('static', filename='img/deadlink.png') }}"
|
||||||
alt="{{ _('Link is dead') }}"
|
alt="{{ _("Link is dead") }}"
|
||||||
height="179"
|
height="179"
|
||||||
width="173"/>
|
width="173" />
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<span>{{ error }}</span>
|
<span>{{ error }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
<a href="{{ url_for('index') }}">
|
<a href="{{ url_for("index") }}">
|
||||||
<i class="fa fa-arrow-left"></i>
|
<i class="fa fa-arrow-left"></i>
|
||||||
{{ _('Back to the beginning') }}
|
{{ _("Back to the beginning") }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<li>
|
<li>
|
||||||
<a href="https://discord.com/users/133305654512320513" target="_blank">
|
<a href="https://discord.com/users/133305654512320513" target="_blank">
|
||||||
<i class="fa fa-user-plus"></i>
|
<i class="fa fa-user-plus"></i>
|
||||||
{{ _('Add author on Discord') }}
|
{{ _("Add author on Discord") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
@ -16,17 +16,17 @@
|
|||||||
<li>
|
<li>
|
||||||
<a href="https://git.crystalyx.net/Xefir/Divent" target="_blank">
|
<a href="https://git.crystalyx.net/Xefir/Divent" target="_blank">
|
||||||
<i class="fa fa-code-fork"></i>
|
<i class="fa fa-code-fork"></i>
|
||||||
{{ _('View the source code') }}
|
{{ _("View the source code") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://hub.docker.com/r/xefir/divent" target="_blank">
|
<a href="https://hub.docker.com/r/xefir/divent" target="_blank">
|
||||||
<i class="fa fa-cubes"></i>
|
<i class="fa fa-cubes"></i>
|
||||||
{{ _('Host it yourself') }}
|
{{ _("Host it yourself") }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa fa-heartbeat"></i>
|
<i class="fa fa-heartbeat"></i>
|
||||||
{{ _('Next castastrophic life failure in about %days% days') | replace('%days%', days_before_failure) }}
|
{{ _("Next castastrophic life failure in about %days% days") | replace('%days%', days_before_failure) }}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -4,31 +4,26 @@
|
|||||||
<div id="box">
|
<div id="box">
|
||||||
<div id="avatars">
|
<div id="avatars">
|
||||||
<img src="{{ client.user.display_avatar }}"
|
<img src="{{ client.user.display_avatar }}"
|
||||||
alt="{{ _('Bot Logo') }}"
|
alt="{{ _("Bot Logo") }}"
|
||||||
width="80"
|
width="80"
|
||||||
height="80"/>
|
height="80" />
|
||||||
<span id="dots">…</span>
|
<span id="dots">…</span>
|
||||||
<img src="{{ cdn_avatar_url(user.id, user.avatar) }}"
|
<img src="{{ avatar.url }}"
|
||||||
alt="{{ _('User Avatar') }}"
|
alt="{{ _("User Avatar") }}"
|
||||||
width="80"
|
width="80"
|
||||||
height="80"/>
|
height="80" />
|
||||||
</div>
|
</div>
|
||||||
<h1>
|
<h1>
|
||||||
<a href="{{ url_for(".index") }}">{{ client.user.display_name }}</a>
|
<a href="{{ url_for(".index") }}">{{ client.user.display_name }}</a>
|
||||||
</h1>
|
</h1>
|
||||||
<a class="button" href="{{ url_for(".guilds", guild=user.id) }}">
|
<h2>{{ _("The discord scheduled event calendar generator") }}</h2>
|
||||||
{{ _("For all your servers") }}
|
<hr>
|
||||||
</a>
|
<a class="button" href="{{ url_for(".guilds", guild=user.id) }}">{{ _("For all your servers") }}</a>
|
||||||
<div class="hr-sect">{{ _("OR") }}</div>
|
<div class="hr-sect">{{ _("OR") }}</div>
|
||||||
<h3>{{ _('Choose a server:') }}</h3>
|
|
||||||
<select name="guild" class="black_input" onchange="this.form.submit()">
|
<select name="guild" class="black_input" onchange="this.form.submit()">
|
||||||
<option>
|
<option> </option>
|
||||||
|
|
||||||
</option>
|
|
||||||
{% for guild in common_guilds %}
|
{% for guild in common_guilds %}
|
||||||
<option value="{{ guild.vanity_url_code|default(guild.id, True) }}">
|
<option value="{{ guild.vanity_url_code|default(guild.id, True) }}">{{ guild.name }}</option>
|
||||||
{{ guild.name }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<div class="hr-sect">{{ _("OR") }}</div>
|
<div class="hr-sect">{{ _("OR") }}</div>
|
||||||
|
@ -4,24 +4,24 @@
|
|||||||
<div id="box">
|
<div id="box">
|
||||||
<div id="avatars">
|
<div id="avatars">
|
||||||
<img src="{{ client.user.display_avatar }}"
|
<img src="{{ client.user.display_avatar }}"
|
||||||
alt="{{ _('Bot Logo') }}"
|
alt="{{ _("Bot Logo") }}"
|
||||||
width="80"
|
width="80"
|
||||||
height="80"/>
|
height="80" />
|
||||||
</div>
|
</div>
|
||||||
<h1>
|
<h1>
|
||||||
<a href="{{ url_for(".index") }}">{{ client.user.display_name }}</a>
|
<a href="{{ url_for(".index") }}">{{ client.user.display_name }}</a>
|
||||||
</h1>
|
</h1>
|
||||||
<h2>{{ _('The discord scheduled event calendar generator') }}</h2>
|
<h2>{{ _("The discord scheduled event calendar generator") }}</h2>
|
||||||
<hr />
|
<hr />
|
||||||
<h3>{{ _('This will allow you to:') }}</h3>
|
<h3>{{ _("This will allow you to:") }}</h3>
|
||||||
<ul id="scopes">
|
<ul id="scopes">
|
||||||
<li>
|
<li>
|
||||||
<i class="fa fa-custom-circle fa-check"></i>
|
<i class="fa fa-custom-circle fa-check"></i>
|
||||||
{{ _('Subscribe to a calendar on Google, Outlook, Apple or any ICS complient software') }}
|
{{ _("Subscribe to a calendar on Google, Outlook, Apple or any ICS complient software") }}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa fa-custom-circle fa-times"></i>
|
<i class="fa fa-custom-circle fa-times"></i>
|
||||||
{{ _('Throwing you to a new isekai world') }}
|
{{ _("Throwing you to a new isekai world") }}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,25 +3,25 @@
|
|||||||
<div id="box">
|
<div id="box">
|
||||||
<div id="avatars">
|
<div id="avatars">
|
||||||
<img src="{{ client.user.display_avatar }}"
|
<img src="{{ client.user.display_avatar }}"
|
||||||
alt="{{ _('Bot Logo') }}"
|
alt="{{ _("Bot Logo") }}"
|
||||||
width="80"
|
width="80"
|
||||||
height="80"/>
|
height="80" />
|
||||||
<span id="dots">…</span>
|
<span id="dots">…</span>
|
||||||
<img src="{{ guild.icon.url }}"
|
<img src="{{ avatar.url }}"
|
||||||
alt="{{ _('Guild Logo') }}"
|
alt="{{ _("Avatar") }}"
|
||||||
width="80"
|
width="80"
|
||||||
height="80"/>
|
height="80" />
|
||||||
</div>
|
</div>
|
||||||
<h1>
|
<h1>
|
||||||
<a href="{{ url_for(".index") }}">{{ client.user.display_name }}</a>
|
<a href="{{ url_for(".index") }}">{{ client.user.display_name }}</a>
|
||||||
</h1>
|
</h1>
|
||||||
<h2>{{ _('The discord scheduled event calendar generator') }}</h2>
|
<h2>{{ _("The discord scheduled event calendar generator") }}</h2>
|
||||||
<hr />
|
<hr />
|
||||||
<ul id="providers">
|
<ul id="providers">
|
||||||
<li>
|
<li>
|
||||||
<a class="button"
|
<a class="button"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://calendar.google.com/calendar/u/0/r?cid=webcal://{{ request.host }}/{{ guild.vanity_url_code|default(guild.id, True) }}.ics">
|
href="https://calendar.google.com/calendar/u/0/r?cid=webcal://{{ request.host }}/{{ entity_id }}.ics">
|
||||||
<i class="fa fa-google"></i>
|
<i class="fa fa-google"></i>
|
||||||
{{ _("Subscribe to") }} Google
|
{{ _("Subscribe to") }} Google
|
||||||
</a>
|
</a>
|
||||||
@ -29,14 +29,14 @@
|
|||||||
<li>
|
<li>
|
||||||
<a class="button"
|
<a class="button"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://outlook.live.com/owa?path=/calendar/action/compose&rru=addsubscription&url=webcal://{{ request.host }}/{{ guild.vanity_url_code|default(guild.id, True) }}.ics">
|
href="https://outlook.live.com/owa?path=/calendar/action/compose&rru=addsubscription&url=webcal://{{ request.host }}/{{ entity_id }}.ics">
|
||||||
<i class="fa fa-windows"></i>
|
<i class="fa fa-windows"></i>
|
||||||
{{ _("Subscribe to") }} Outlook
|
{{ _("Subscribe to") }} Outlook
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{# djlint:off #}
|
{# djlint:off #}
|
||||||
<a class="button" target="_blank" href="webcal://{{ request.host }}/{{ guild.vanity_url_code|default(guild.id, True) }}.ics">
|
<a class="button" target="_blank" href="webcal://{{ request.host }}/{{ entity_id }}.ics">
|
||||||
<i class="fa fa-apple"></i>
|
<i class="fa fa-apple"></i>
|
||||||
{{ _("Subscribe to") }} Apple
|
{{ _("Subscribe to") }} Apple
|
||||||
</a>
|
</a>
|
||||||
@ -47,7 +47,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<h3>{{ _("Use the direct link:") }}</h3>
|
<h3>{{ _("Use the direct link:") }}</h3>
|
||||||
{# djlint:off #}
|
{# djlint:off #}
|
||||||
<input type="text" readonly class="black_input" value="webcal://{{ request.host }}/{{ guild.vanity_url_code|default(guild.id, True) }}.ics"/>
|
<input type="text" readonly class="black_input" value="webcal://{{ request.host }}/{{ entity_id }}.ics"/>
|
||||||
{# djlint:on #}
|
{# djlint:on #}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
"Subscribe to a calendar on Google, Outlook, Apple or any ICS complient software": "T'abonner à un calendrier sur Google, Outlook, Apple ou tout autre logiciel compatible",
|
"Subscribe to a calendar on Google, Outlook, Apple or any ICS complient software": "T'abonner à un calendrier sur Google, Outlook, Apple ou tout autre logiciel compatible",
|
||||||
"Throwing you to a new isekai world": "T'envoyer dans un monde fantaisiste armée d'une poêle à frire",
|
"Throwing you to a new isekai world": "T'envoyer dans un monde fantaisiste armée d'une poêle à frire",
|
||||||
"For all your servers": "Pour tous tes serveurs",
|
"For all your servers": "Pour tous tes serveurs",
|
||||||
"Choose a server:": "Choisi un serveur :",
|
|
||||||
"You must have": "Tu dois avoir la permission",
|
"You must have": "Tu dois avoir la permission",
|
||||||
"Manage Server": "Gérer le serveur",
|
"Manage Server": "Gérer le serveur",
|
||||||
"permission on this server to perform this action": "sur ce serveur pour effectuer cette action",
|
"permission on this server to perform this action": "sur ce serveur pour effectuer cette action",
|
||||||
|
Loading…
Reference in New Issue
Block a user