mutubot/main.py
2024-11-07 21:04:04 +01:00

211 lines
6.7 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import discord
import yaml
import requests
from datetime import date
import urllib.parse
# To send discord messages (fucking async functions…)
import asyncio
from apscheduler.schedulers.background import BackgroundScheduler
scheduler = BackgroundScheduler()
scheduler.start()
from dotenv import load_dotenv
load_dotenv()
from email.message import EmailMessage
import smtplib, ssl
# Create a secure SSL context
ssl_context = ssl.create_default_context()
def load_guilds_data():
with open("guilds.yml", 'r') as stream:
try:
return yaml.safe_load(stream)
except yaml.YAMLError as exc:
print(exc)
def send_mass_mail(guild, subject, content):
send_mail(guild, guild['mailing'], subject, content)
def send_mail(guild, to, subject, content):
msg = EmailMessage()
msg['Subject'] = subject
msg['From'] = guild['mail_from']
msg['To'] = to
msg.set_content(content)
with smtplib.SMTP_SSL(guild['smtp_host'], guild['smtp_port'], context=ssl_context) as server:
server.login(guild['smtp_username'], guild['smtp_pass'])
server.send_message(msg)
def mail_message(message):
send_mass_mail(guilds[message.guild.id], f'[Mutubot] Nouveau message discord de {message.author.display_name}', f'{message.author.display_name}:\n{message.content}')
def req(url, data):
x = requests.post(url, headers={'Content-Type': 'application/x-www-form-urlencoded'}, data=data)
if x.status_code != 200:
print('ERROR', x)
print(x.content)
raise Exception('Request error ' + str(x.status_code))
# Load some data
TOKEN = os.getenv('DISCORD_TOKEN')
guilds = load_guilds_data()
reminder_channels = []
for i in guilds:
if 'reminder_channel' in guilds[i]:
reminder_channels.append(guilds[i]['reminder_channel'])
from html.parser import HTMLParser
class TokenFinder(HTMLParser):
def __init__(self, admin_url):
self.token = None
self.public_link = None
self.delete_links = []
self.public_links = []
self.admin_url = admin_url
super().__init__()
def handle_starttag(self, tag, attrs_tuple):
if tag != 'input' and tag != 'a':
return
attrs = dict(attrs_tuple)
if tag == 'input' and 'type' in attrs and attrs['type'] == 'hidden' and 'name' in attrs and attrs['name'] == 'control' and 'value' in attrs :
self.token = attrs['value']
elif tag == 'input' and 'id' in attrs and attrs['id'] == 'public-link' and 'value' in attrs :
self.public_link = attrs['value']
elif tag == 'a' and 'href' in attrs and attrs['href'].startswith(self.admin_url + '/action/delete_column/') :
self.delete_links.append(attrs['href'])
elif tag == 'a' and 'href' in attrs and self.public_link and attrs['href'].startswith(self.public_link + '/vote/') :
self.public_links.append(attrs['href'])
def scrap_framavote (admin_url):
# Parse html to find data
finder = TokenFinder(admin_url)
finder.feed(requests.get(admin_url).content.decode('UTF-8'))
return finder
def create_framavote (guild, names):
finder = scrap_framavote(guild['framavote'])
# Remove everything
erase_framadate(guild['framavote'], finder.delete_links)
# Add columns
for name in names:
req(guild['framavote'], 'choice='+name+'&confirm_add_column=')
# Update control sum
finder = scrap_framavote(guild['framavote'])
# Add lines
for i in range(len(guild['members'])):
create_line_framadate(finder.token, guild['framavote'], 'AnneONyme'+str(i), names)
# Update links
finder = scrap_framavote(guild['framavote'])
# Send links
for mail,link in zip(guild['members'], finder.public_links):
content = f"""
Ce mail remplace tous les précédents !
Voici votre lien de vote anonyme :
{link}
"""
send_mail(guild, mail, '[Mutubot] Votre lien de vote anonyme', content)
return finder.public_link
def erase_framadate (admin_url, delete_links=[]):
req(admin_url, 'remove_all_votes=')
req(admin_url, 'confirm_remove_all_votes=')
for link in delete_links:
requests.get(link)
def create_line_framadate (token, admin_url, voter, names):
data = 'control=' + token + '&name=' + voter + ''.join('&choices%5B'+str(i)+'%5D=+' for i in range(len(names))) + '&save='
x = req(admin_url, data)
@scheduler.scheduled_job('cron', day=25)
def cleaner ():
for guild_id in guilds:
erase_framadate(guilds[guild_id]['framadate_week'])
erase_framadate(guilds[guild_id]['framadate_weekend'])
# TODO erase calc revenus ?
@scheduler.scheduled_job('cron', day=1)
def reminder ():
for i in guilds:
print(f"reminding {i} : {guilds[i]['mailing']}")
message = generate_reminder_message(guilds[i])
send_mass_mail(guilds[i], '[Mutubot] La mutunion cest bientôt !', message)
channel = client.get_channel(guilds[i]['reminder_channel'])
asyncio.run_coroutine_threadsafe(channel.send(message), client.loop)
def generate_reminder_message (guild):
# If the 10 is weekend
sondage = guild['framadate_week']
if date.today().replace(day=10).weekday() > 4:
sondage = guild['framadate_weekend']
return f"""
Coucou !
Il est lheure de déclarer ses revenus :
<{guild['link_declaration']}>
Et dannoncer à quelle heure vous souhaitez faire la mutunion :
<{sondage}>
Bon début de mois :D
Le mutubot
"""
# Discord API
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
randomvote = None
@client.event
async def on_ready():
print(f'{client.user} is connected to the following guild:\n')
for guild in client.guilds:
print(f'{guild.name} (id: {guild.id})')
@client.event
async def on_message(message):
global randomvote
if message.author == client.user:
return
if message.guild.id not in guilds:
return
if message.content.startswith('!randomvote '):
await message.reply('Cela va détruire le framavote actuel. Êtes vous sûr·e ? Répondez « Pamplemousse agrivoltaiste » pour confirmer')
randomvote = message.content.split(' ')[1:]
return
elif message.content == 'Pamplemousse agrivoltaiste' and message.type == discord.MessageType.reply :
public_link = create_framavote(guilds[message.guild.id], randomvote)
randomvote = None
await message.reply("Cest fait ! Vérifiez bien par vous même parce que je suis un peu fini à larache… " + public_link)
return
elif message.channel.id in guilds[message.guild.id]['mailed_channels']:
mail_message(message)
# Actually starts the bot
client.run(TOKEN)