Dennis
eb2b887c0a
All configuration files and all messages files should now preferrable be done in Hjson. This format supports, i.a. features, multi line strings and commenting. This greatly improves readability. All installation and documentation files are also updated in this commit.
264 lines
8.7 KiB
Python
264 lines
8.7 KiB
Python
__author__ = "Dennis Potter"
|
|
__copyright__ = "Copyright 2019, Dennis Potter"
|
|
__credits__ = ["Dennis Potter"]
|
|
__license__ = "GPL-3.0"
|
|
__version__ = "0.5.0"
|
|
__maintainer__ = "Dennis Potter"
|
|
__email__ = "dennis@dennispotter.eu"
|
|
|
|
import os
|
|
import hjson
|
|
import time
|
|
import sqlite3
|
|
import threading
|
|
import datetime as dt
|
|
import MySQLdb as mysql
|
|
|
|
from matrix_bot_api.mregex_handler import MRegexHandler
|
|
|
|
MESSAGES_DIR = os.path.join(os.path.dirname(__file__), 'messages')
|
|
DATA_DIR = os.path.join(os.path.dirname(__file__),'../../data/events')
|
|
CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.hjson')
|
|
|
|
DATA_LOCATION = DATA_DIR + '/data.db'
|
|
HELP_LOCATION = MESSAGES_DIR + '/help'
|
|
MESSAGES_LOCATION = MESSAGES_DIR + '/messages.dutch.hjson'
|
|
|
|
class Plugin:
|
|
""" This plugin grabs events from Admidio (https://admidio.org)
|
|
and lets users interact with the data
|
|
"""
|
|
def __init__(self, bot):
|
|
# Load the configuration
|
|
with open(CONFIG_LOCATION) as hjson_data:
|
|
self.config = hjson.load(hjson_data)
|
|
|
|
# Load all messages for this plugin
|
|
with open(MESSAGES_LOCATION) as hjson_data:
|
|
self.messages = hjson.load(hjson_data)
|
|
|
|
# Define sensitivity
|
|
self.handler = []
|
|
|
|
self.handler.append(MRegexHandler("Peter evenementen lijst",
|
|
self.list_callback))
|
|
self.handler.append(MRegexHandler("Peter evenementen info",
|
|
self.info_callback))
|
|
self.handler.append(MRegexHandler("Peter evenementen chat",
|
|
self.chat_callback))
|
|
self.handler.append(MRegexHandler("Peter evenementen deelnemers",
|
|
self.participants_callback))
|
|
|
|
# Save parent bot
|
|
self.bot = bot
|
|
|
|
# Start thread to check events
|
|
self.event_thread = threading.Thread(target=self.check_new_event_thread)
|
|
self.event_thread.start()
|
|
|
|
def connect_database(self):
|
|
# Connect to database
|
|
return mysql.connect(
|
|
user = self.config['database_credentials']['username'],
|
|
password = self.config['database_credentials']['password'],
|
|
database = self.config['database_credentials']['database'])
|
|
|
|
|
|
def check_new_event_thread(self):
|
|
while not self.bot.cancel:
|
|
self.check_new_event()
|
|
|
|
def check_new_event(self):
|
|
""" Check every <X> minutes for new events in the database.
|
|
|
|
Since the check will only be performed every <X> minutes, the
|
|
connection to the database will not be kept open.
|
|
|
|
As soon as an event is posted to the defined room, its ID is
|
|
saved in an SQLite database.
|
|
"""
|
|
|
|
mysql_db = self.connect_database()
|
|
mysql_cursor = mysql_db.cursor()
|
|
|
|
# Grab all events from database that start in the future
|
|
select_sql = """SELECT dat_id, dat_cat_id,
|
|
dat_headline, dat_begin, dat_end
|
|
FROM adm_dates
|
|
WHERE dat_begin >'{}'""".format(
|
|
dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
|
|
|
mysql_cursor.execute(select_sql)
|
|
|
|
# Fetch events
|
|
results = mysql_cursor.fetchall()
|
|
|
|
# Open SQLite database
|
|
sqlite_db = sqlite3.connect(DATA_LOCATION)
|
|
|
|
sqlite_cursor = sqlite_db.cursor()
|
|
|
|
# Define query to SELECT event from SQLite DB
|
|
select_sql = """SELECT dat_id
|
|
FROM events
|
|
WHERE dat_id = {}"""
|
|
|
|
insert_sql = """INSERT INTO events(dat_id, datetime_posted)
|
|
VALUES(:dat_id, :datetime_posted)"""
|
|
|
|
# Loop through event
|
|
for row in results:
|
|
# First, check if a message on this event was already sent
|
|
sqlite_cursor.execute(select_sql.format(row[0]))
|
|
|
|
if sqlite_cursor.fetchall():
|
|
# Do nothing. This event was already processed
|
|
pass
|
|
else:
|
|
# This appears to be a new event. Process it!
|
|
|
|
# Generate links
|
|
base_date_url = "{}{}".format(
|
|
self.config['base_url_adm'],
|
|
"/adm_program/modules/dates/")
|
|
|
|
view_link = "{}dates.php?id={}&view_mode=html&"
|
|
view_link += "view=detail&headline={}"
|
|
view_link = view_link.format(base_date_url, row[0], row[2])
|
|
|
|
function_link = "{}dates_function.php?mode={}&dat_id={}&"
|
|
|
|
attend_link = function_link.format(base_date_url, '3', row[0])
|
|
maybe_link = function_link.format(base_date_url, '7', row[0])
|
|
cancel_link = function_link.format(base_date_url, '4', row[0])
|
|
|
|
# First, write to SQLite database that it is processed
|
|
sqlite_cursor.execute(
|
|
insert_sql, {
|
|
'dat_id':row[0],
|
|
'datetime_posted':
|
|
dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S")})
|
|
|
|
# Check role ID of event and check if config defines
|
|
# where it should be shared.
|
|
for mapping in self.config['cat_id_room_mapping']:
|
|
if row[1] in mapping[0]:
|
|
# We got a winner!
|
|
|
|
for i, room in self.bot.client.get_rooms().items():
|
|
if room.display_name in mapping[1]:
|
|
room.send_html(
|
|
self.messages["new_event"].format(
|
|
row[3].strftime("%d-%m-%Y"),
|
|
row[3].strftime("%H:%M"),
|
|
row[2],
|
|
view_link,
|
|
attend_link,
|
|
maybe_link,
|
|
cancel_link))
|
|
|
|
# Commit and close queries
|
|
sqlite_db.commit()
|
|
sqlite_db.close()
|
|
|
|
# Close MySQL
|
|
mysql_db.close()
|
|
|
|
# Sleep
|
|
time.sleep(self.config['update_time_span'] * 60)
|
|
|
|
def list_callback(self, room, event):
|
|
number_events = False
|
|
|
|
for word in event['content']['body'].split():
|
|
try:
|
|
number_events = int(word)
|
|
break
|
|
except:
|
|
pass
|
|
|
|
if not number_events:
|
|
number_events = 10
|
|
elif number_events <= 0 or number_events > 100:
|
|
room.send_text(self.messages['list_event_num_err'])
|
|
number_events = 10
|
|
|
|
datetime_now = dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
mysql_db = self.connect_database()
|
|
mysql_cursor = mysql_db.cursor()
|
|
|
|
select_sql = f"""SELECT dat_id, dat_cat_id,
|
|
dat_headline, dat_begin, dat_end
|
|
FROM adm_dates
|
|
WHERE dat_begin >'{datetime_now}'
|
|
ORDER BY dat_begin
|
|
LIMIT {number_events}"""
|
|
|
|
mysql_cursor.execute(select_sql)
|
|
|
|
# Fetch events
|
|
results = mysql_cursor.fetchall()
|
|
|
|
# Set header
|
|
html_message = self.messages['list_events_head'].format(number_events)
|
|
|
|
# Loop through fetched events
|
|
html_message += "<br /><ul>"
|
|
for row in results:
|
|
base_date_url = "{}{}".format(
|
|
self.config['base_url_adm'],
|
|
"/adm_program/modules/dates/")
|
|
|
|
function_link = "{}dates_function.php?mode={}&dat_id={}&"
|
|
|
|
attend_link = function_link.format(base_date_url, '3', row[0])
|
|
|
|
html_message += "<li>"
|
|
html_message += self.messages['list_event_item'].format(
|
|
row[3].strftime("%d-%m-%Y"),
|
|
row[3].strftime("%H:%M"),
|
|
row[2],
|
|
attend_link)
|
|
html_message += "</li>"
|
|
|
|
html_message += "</ul>"
|
|
|
|
# Send message
|
|
room.send_html(html_message)
|
|
|
|
# Close DB connection
|
|
mysql_db.close()
|
|
|
|
def info_callback(self, room, event):
|
|
room.send_text("Info")
|
|
|
|
def chat_callback(self, room, event):
|
|
room.send_text("Chat")
|
|
|
|
def participants_callback(self, room, event):
|
|
room.send_text("Deelnemers")
|
|
|
|
def help(self):
|
|
return open(HELP_LOCATION, mode="r").read()
|
|
|
|
def setup():
|
|
"""This function runs only, ever. It initializes the necessary
|
|
SQLite tables."""
|
|
|
|
# Try to make a new directory
|
|
os.mkdir(DATA_DIR)
|
|
|
|
# Define query to INSERT event table to SQLite DB
|
|
sql = """CREATE TABLE 'events' (
|
|
'dat_id' integer,
|
|
'datetime_posted' datetime);"""
|
|
|
|
# Open, execute, commit, and close SQLite database
|
|
sqlite_db = sqlite3.connect(DATA_LOCATION)
|
|
sqlite_cursor = sqlite_db.cursor()
|
|
sqlite_cursor.execute(sql)
|
|
sqlite_db.commit()
|
|
sqlite_db.close()
|
|
|