391 lines
10 KiB
Python
391 lines
10 KiB
Python
|
#!/usr/bin/env python
|
||
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
from datetime import datetime, timezone
|
||
|
import time
|
||
|
import threading
|
||
|
import os
|
||
|
import sys
|
||
|
import os.path
|
||
|
from email.mime.multipart import MIMEMultipart
|
||
|
from email.mime.text import MIMEText
|
||
|
import smtplib
|
||
|
from smtplib import SMTPException, SMTPAuthenticationError, SMTPConnectError, SMTPRecipientsRefused
|
||
|
import psycopg2
|
||
|
import socket
|
||
|
from socket import gaierror
|
||
|
|
||
|
###################################################################################
|
||
|
# write to database mailing status of inactive users
|
||
|
###################################################################################
|
||
|
|
||
|
def write_db(now, id, username, email, emailed_at, emailed):
|
||
|
|
||
|
insert_line = "INSERT INTO " + mailing_db_table + "(datetime, account_id, username, email, emailed_at, emailed) VALUES(%s,%s,%s,%s,%s,%s) ON CONFLICT DO NOTHING"
|
||
|
|
||
|
conn = None
|
||
|
|
||
|
try:
|
||
|
|
||
|
conn = psycopg2.connect(database = mailing_db, user = mailing_db_user, password = "", host = "/var/run/postgresql", port = "5432")
|
||
|
|
||
|
cur = conn.cursor()
|
||
|
|
||
|
cur.execute("SELECT account_id FROM " + mailing_db_table + " where account_id=(%s)", (id,))
|
||
|
|
||
|
row = cur.fetchone()
|
||
|
|
||
|
if row == None:
|
||
|
|
||
|
cur.execute(insert_line, (now, id, username, email, now, emailed))
|
||
|
|
||
|
else:
|
||
|
|
||
|
if emailed == True:
|
||
|
|
||
|
cur.execute("SELECT datetime FROM " + mailing_db_table + " where account_id=(%s)", (id,))
|
||
|
|
||
|
row = cur.fetchone()
|
||
|
delta = now-row[0]
|
||
|
|
||
|
cur.execute("UPDATE " + mailing_db_table + " SET elapsed_days=(%s), email=(%s), emailed=(%s) where account_id=(%s)", (delta, email, emailed, id))
|
||
|
|
||
|
conn.commit()
|
||
|
|
||
|
cur.close()
|
||
|
|
||
|
except (Exception, psycopg2.DatabaseError) as error:
|
||
|
|
||
|
print(error)
|
||
|
|
||
|
finally:
|
||
|
|
||
|
if conn is not None:
|
||
|
|
||
|
conn.close()
|
||
|
|
||
|
###############################################################################
|
||
|
# check if inactive user had been already emailed
|
||
|
###############################################################################
|
||
|
|
||
|
def email_sent(id):
|
||
|
|
||
|
try:
|
||
|
|
||
|
conn = psycopg2.connect(database = mailing_db, user = mailing_db_user, password = "", host = "/var/run/postgresql", port = "5432")
|
||
|
|
||
|
cur = conn.cursor()
|
||
|
|
||
|
cur.execute("SELECT emailed FROM " + mailing_db_table + " where account_id=(%s)", (id,))
|
||
|
|
||
|
row = cur.fetchone()
|
||
|
|
||
|
if row == None:
|
||
|
|
||
|
been_emailed = False
|
||
|
|
||
|
else:
|
||
|
|
||
|
been_emailed = row[0]
|
||
|
|
||
|
cur.close()
|
||
|
|
||
|
return been_emailed
|
||
|
|
||
|
except (Exception, psycopg2.DatabaseError) as error:
|
||
|
|
||
|
print(error)
|
||
|
|
||
|
finally:
|
||
|
|
||
|
if conn is not None:
|
||
|
|
||
|
conn.close()
|
||
|
|
||
|
###############################################################################
|
||
|
# Connect to Mastodon's Postgres DB to check if any inactive user is back online
|
||
|
###############################################################################
|
||
|
|
||
|
def check_alive(id):
|
||
|
|
||
|
try:
|
||
|
|
||
|
conn = psycopg2.connect(database = mastodon_db, user = mastodon_db_user, password = "", host = "/var/run/postgresql", port = "5432")
|
||
|
|
||
|
cur = conn.cursor()
|
||
|
|
||
|
cur.execute("select last_sign_in_at from users where account_id=(%s)", (id,))
|
||
|
|
||
|
row = cur.fetchone()
|
||
|
seen = row[0]
|
||
|
|
||
|
cur.close()
|
||
|
|
||
|
return seen
|
||
|
|
||
|
except (Exception, psycopg2.DatabaseError) as error:
|
||
|
|
||
|
print(error)
|
||
|
|
||
|
###############################################################################
|
||
|
# INITIALISATION
|
||
|
###############################################################################
|
||
|
|
||
|
# Returns the parameter from the specified file
|
||
|
def get_parameter( parameter, file_path ):
|
||
|
# Check if secrets file exists
|
||
|
if not os.path.isfile(file_path):
|
||
|
if file_path == "secrets/secrets.txt":
|
||
|
print("File %s not found, exiting. Run setup.py."%file_path)
|
||
|
elif file_path == "config.txt":
|
||
|
print("File %s not found, exiting. Run db-setup.py."%file_path)
|
||
|
sys.exit(0)
|
||
|
|
||
|
# Find parameter in file
|
||
|
with open( file_path ) as f:
|
||
|
for line in f:
|
||
|
if line.startswith( parameter ):
|
||
|
return line.replace(parameter + ":", "").strip()
|
||
|
|
||
|
# Cannot find parameter, exit
|
||
|
print(file_path + " Missing parameter %s "%parameter)
|
||
|
print("Run setup.py")
|
||
|
sys.exit(0)
|
||
|
|
||
|
# Load secrets from secrets file
|
||
|
secrets_filepath = "secrets/secrets.txt"
|
||
|
smtp_host = get_parameter("smtp_host", secrets_filepath)
|
||
|
smtp_user_login = get_parameter("smtp_user_login", secrets_filepath)
|
||
|
smtp_user_password = get_parameter("smtp_user_password", secrets_filepath)
|
||
|
email_subject = get_parameter("email_subject", secrets_filepath)
|
||
|
|
||
|
# Load configuration from config file
|
||
|
config_filepath = "config.txt"
|
||
|
mastodon_db = get_parameter("mastodon_db", config_filepath)
|
||
|
mastodon_db_user = get_parameter("mastodon_db_user", config_filepath)
|
||
|
mailing_db = get_parameter("mailing_db", config_filepath)
|
||
|
mailing_db_user = get_parameter("mailing_db_user", config_filepath)
|
||
|
mailing_db_table = get_parameter("mailing_db_table", config_filepath)
|
||
|
|
||
|
###############################################################################
|
||
|
# check if inactive user is back online
|
||
|
###############################################################################
|
||
|
|
||
|
try:
|
||
|
|
||
|
conn = psycopg2.connect(database = mailing_db, user = mailing_db_user, password = "", host = "/var/run/postgresql", port = "5432")
|
||
|
|
||
|
cur = conn.cursor()
|
||
|
|
||
|
cur.execute("SELECT account_id FROM " + mailing_db_table)
|
||
|
|
||
|
rows = cur.fetchall()
|
||
|
if rows != []:
|
||
|
|
||
|
inactive_users_id = []
|
||
|
|
||
|
for row in rows:
|
||
|
|
||
|
inactive_users_id.append(row[0])
|
||
|
|
||
|
else:
|
||
|
|
||
|
inactive_users_id = []
|
||
|
|
||
|
cur.close()
|
||
|
|
||
|
except (Exception, psycopg2.DatabaseError) as error:
|
||
|
|
||
|
print(error)
|
||
|
|
||
|
finally:
|
||
|
|
||
|
if conn is not None:
|
||
|
|
||
|
conn.close()
|
||
|
|
||
|
i = 0
|
||
|
while i < len(inactive_users_id):
|
||
|
|
||
|
seen = check_alive(inactive_users_id[i])
|
||
|
|
||
|
try:
|
||
|
|
||
|
conn = psycopg2.connect(database = mailing_db, user = mailing_db_user, password = "", host = "/var/run/postgresql", port = "5432")
|
||
|
|
||
|
cur = conn.cursor()
|
||
|
|
||
|
cur.execute("SELECT emailed_at FROM " + mailing_db_table + " where account_id=(%s)", (inactive_users_id[i],))
|
||
|
|
||
|
row = cur.fetchone()
|
||
|
email_datetime = row[0]
|
||
|
email_datetime = email_datetime.replace(tzinfo=None)
|
||
|
|
||
|
if email_datetime < seen:
|
||
|
|
||
|
cur.execute("DELETE FROM " + mailing_db_table + " where account_id=(%s)", (inactive_users_id[i],))
|
||
|
|
||
|
conn.commit()
|
||
|
|
||
|
cur.close()
|
||
|
|
||
|
i += 1
|
||
|
|
||
|
except (Exception, psycopg2.DatabaseError) as error:
|
||
|
|
||
|
print(error)
|
||
|
|
||
|
finally:
|
||
|
|
||
|
if conn is not None:
|
||
|
|
||
|
conn.close()
|
||
|
|
||
|
###############################################################################
|
||
|
|
||
|
now = datetime.now(timezone.utc)
|
||
|
|
||
|
###############################################################################
|
||
|
# Connect to Mastodon's Postgres DB to get last year inactive users
|
||
|
###############################################################################
|
||
|
|
||
|
try:
|
||
|
|
||
|
conn = psycopg2.connect(database = mastodon_db, user = mastodon_db_user, password = "", host = "/var/run/postgresql", port = "5432")
|
||
|
|
||
|
cur = conn.cursor()
|
||
|
|
||
|
cur.execute("select account_id, email from users where last_sign_in_at < now() - interval '365 days' and disabled=False and approved=True order by last_sign_in_at desc;")
|
||
|
rows = cur.fetchall()
|
||
|
|
||
|
inactive_account_id = []
|
||
|
inactive_email = []
|
||
|
|
||
|
for row in rows:
|
||
|
inactive_account_id.append(row[0])
|
||
|
inactive_email.append(row[1])
|
||
|
|
||
|
cur.close()
|
||
|
|
||
|
except (Exception, psycopg2.DatabaseError) as error:
|
||
|
|
||
|
print(error)
|
||
|
|
||
|
########################################################################################################
|
||
|
# get related usernames #
|
||
|
########################################################################################################
|
||
|
|
||
|
inactive_usernames = []
|
||
|
|
||
|
i = 0
|
||
|
while i < len(inactive_account_id):
|
||
|
|
||
|
try:
|
||
|
|
||
|
conn = psycopg2.connect(database = mastodon_db, user = mastodon_db_user, password = "", host = "/var/run/postgresql", port = "5432")
|
||
|
|
||
|
cur = conn.cursor()
|
||
|
|
||
|
inactive_id = inactive_account_id[i]
|
||
|
cur.execute("select username from accounts where id = '%s';", [inactive_id])
|
||
|
|
||
|
row = cur.fetchone()
|
||
|
new_username = row[0]
|
||
|
inactive_usernames.append(new_username)
|
||
|
|
||
|
i += 1
|
||
|
|
||
|
cur.close()
|
||
|
|
||
|
except (Exception, psycopg2.DatabaseError) as error:
|
||
|
|
||
|
print(error)
|
||
|
|
||
|
finally:
|
||
|
|
||
|
if conn is not None:
|
||
|
|
||
|
conn.close()
|
||
|
|
||
|
###########################################################################################################
|
||
|
# email inactive users
|
||
|
###########################################################################################################
|
||
|
|
||
|
try:
|
||
|
|
||
|
fp = open('message.txt')
|
||
|
text = fp.read()
|
||
|
message = MIMEText(text)
|
||
|
print(type(message))
|
||
|
print(message)
|
||
|
|
||
|
except:
|
||
|
|
||
|
print("message.txt file not found! Create it and write in the message you want for your inactive users.")
|
||
|
|
||
|
finally:
|
||
|
|
||
|
fp.close()
|
||
|
|
||
|
i = 0
|
||
|
while i < len(inactive_email):
|
||
|
|
||
|
been_emailed = email_sent(inactive_account_id[i])
|
||
|
|
||
|
if been_emailed == False:
|
||
|
|
||
|
# Create message object instance
|
||
|
msg = MIMEMultipart()
|
||
|
|
||
|
# Declare message elements
|
||
|
msg['From'] = 'notificacions@mastodont.cat'
|
||
|
msg['To'] = inactive_email[i]
|
||
|
msg['Subject'] = inactive_usernames[i] + " " + email_subject
|
||
|
|
||
|
# Add the message body to the object instance
|
||
|
msg.attach(message)
|
||
|
|
||
|
try:
|
||
|
|
||
|
# Create the server connection
|
||
|
server = smtplib.SMTP(smtp_host)
|
||
|
# Switch the connection over to TLS encryption
|
||
|
server.starttls()
|
||
|
# Authenticate with the server
|
||
|
server.login(smtp_user_login, smtp_user_password)
|
||
|
# Send the message
|
||
|
server.sendmail(msg['From'], msg['To'], msg.as_string())
|
||
|
# Disconnect
|
||
|
server.quit()
|
||
|
|
||
|
print("Successfully sent email message to %s" % msg['To'])
|
||
|
emailed = True
|
||
|
write_db(now, inactive_account_id[i], inactive_usernames[i], inactive_email[i], now, emailed)
|
||
|
|
||
|
i += 1
|
||
|
time.sleep(5)
|
||
|
|
||
|
except SMTPAuthenticationError as auth_error:
|
||
|
|
||
|
print(auth_error)
|
||
|
sys.exit(":-(")
|
||
|
|
||
|
except socket.gaierror as socket_error:
|
||
|
|
||
|
print(socket_error)
|
||
|
print("Unknown SMTP server")
|
||
|
sys.exit(":-(")
|
||
|
|
||
|
except SMTPRecipientsRefused as recip_error:
|
||
|
|
||
|
print(recip_error)
|
||
|
emailed = False
|
||
|
write_db(now, inactive_account_id[i], inactive_usernames[i], inactive_email[i], now, emailed)
|
||
|
i += 1
|
||
|
|
||
|
else:
|
||
|
|
||
|
emailed = True
|
||
|
write_db(now, inactive_account_id[i], inactive_usernames[i], inactive_email[i], now, emailed)
|
||
|
i += 1
|