plugable-matrix-bot/plugins/admidio_events/admidio_events.py
Dennis Potter ca86ade0fa
Made sensitivity configurable and added aliases
Before this commit, the name of the bot had to be hard coded into every
plugin. Now, the name of the bot to which it should be sensitive can be
set in the global config.hjson.

Furthermore, the default sensitivity of a plugin is only recognized at
the beginning of a sentence and is case insensitive.

Finally, the possibility to add aliases for plugins is added.
2019-02-27 19:08:09 +01:00

374 lines
12 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 importlib
import sys
import datetime as dt
from matrix_bot_api.mregex_handler import MRegexHandler
sys.path.insert(0, os.path.dirname(__file__))
import admidio_python_api.admidio as admidio
import admidio_python_api.group as group
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)
self.adm = admidio.Admidio(
self.config['database_credentials']['host'],
self.config['database_credentials']['username'],
self.config['database_credentials']['password'],
self.config['database_credentials']['database'],
self.config['database_credentials']['adm_prefix'])
# Define sensitivity
self.handler = []
self.handler.append(MRegexHandler(
"Peter evenementen lijst",
self.list_callback,
bot))
self.handler.append(MRegexHandler(
"Peter evenementen info",
self.info_callback,
bot))
self.handler.append(MRegexHandler(
"Peter evenementen chat",
self.chat_callback,
bot))
self.handler.append(MRegexHandler(
"Peter evenementen deelnemers",
self.participants_callback,
bot))
# 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 ready_block(self):
while (not self.adm.ready):
time.sleep(1)
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.
"""
# Refresh everything
self.adm.refresh()
# 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
now = dt.datetime.now()
events = (x for (k,x) in self.adm.events.items() if x.start_time > now)
for (i, event) in enumerate(events):
# First, check if a message on this event was already sent
sqlite_cursor.execute(select_sql.format(event.id))
if sqlite_cursor.fetchall():
# Do nothing. This event was already processed
pass
else:
# This appears to be a new event. Process it!
# Generate links
links = self.generate_links(event.id, event.name)
# First, write to SQLite database that it is processed
sqlite_cursor.execute(
insert_sql, {
'dat_id':event.id,
'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 event.cat_id in mapping[0]:
# We got a winner!
for i, room in self.bot.client.rooms.items():
if room.display_name in mapping[1]:
room.send_html(
self.messages["new_event"].format(
event.start_time.strftime("%d-%m-%Y"),
event.start_time.strftime("%H:%M"),
event.name,
links['view'],
links['attend'],
links['maybe'],
links['cancel']))
# Commit and close queries
sqlite_db.commit()
sqlite_db.close()
# Sleep
time.sleep(self.config['update_time_span'] * 60)
def generate_links(self, event_id, event_name):
links = dict()
# Define the base URL for users to attend
base_dat_url = "{}{}".format(
self.config['base_url_adm'],
"/adm_program/modules/dates/")
links['view'] = "{}dates.php?id={}&view_mode=html&"
links['view'] += "view=detail&headline={}"
links['view'] = links['view'].format(base_dat_url, event_id, event_name)
function_link = "{}dates_function.php?mode={}&dat_id={}&"
links['attend'] = function_link.format(base_dat_url, '3', event_id)
links['maybe'] = function_link.format(base_dat_url, '7', event_id)
links['cancel'] = function_link.format(base_dat_url, '4', event_id)
return links
def list_callback(self, room, event):
self.ready_block()
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
base_date_url = "{}{}".format(
self.config['base_url_adm'],
"/adm_program/modules/dates/")
function_link = "{}dates_function.php?mode={}&dat_id={}&"
now = dt.datetime.now()
events = (x for (k,x) in self.adm.events.items() if x.start_time > now)
# Set header
html_message = self.messages['list_events_head'].format(number_events)
# Loop through fetched events
html_message += "<br /><ul>"
for (i, event) in enumerate(events):
if (i >= number_events):
break;
# Generate links
links = self.generate_links(event.id, event.name)
html_message += "<li>"
html_message += self.messages['list_event_item'].format(
event.start_time.strftime("%d-%m-%Y"),
event.start_time.strftime("%H:%M"),
links['view'],
event.name,
event.id)
html_message += "</li>"
html_message += "</ul>"
# Send message
room.send_html(html_message)
def info_callback(self, room, event):
self.ready_block()
id_event = False
for word in event['content']['body'].split():
try:
id_event = int(word)
break
except:
pass
if not id_event or id_event < 0:
room.send_html(self.messages['info_event_id_err'])
return
try:
# Generate links
links = self.generate_links(
self.adm.events[id_event].id,
self.adm.events[id_event].name)
# Assemble message
html_message = self.messages['info_event'].format(
links['view'],
self.adm.events[id_event].name,
self.adm.events[id_event].start_time.strftime("%d-%m-%Y"),
self.adm.events[id_event].start_time.strftime("%H:%M"),
self.adm.events[id_event].end_time.strftime("%d-%m-%Y"),
self.adm.events[id_event].end_time.strftime("%H:%M"),
self.adm.events[id_event].description,
links['attend'],
links['maybe'],
links['cancel'])
# Send message
room.send_html(html_message)
except KeyError:
room.send_html(self.messages['unknown_event'])
def participants_callback(self, room, event):
self.ready_block()
id_event = False
for word in event['content']['body'].split():
try:
id_event = int(word)
break
except:
pass
if not id_event or id_event < 0:
room.send_html(self.messages['info_event_id_err'])
return
try:
# Set header
html_message = self.messages['event_participants_head'].format(
len(self.adm.events[id_event].getAllAttend()),
self.adm.events[id_event].number_of_guests,
self.adm.events[id_event].name)
# Attend
html_message += "<br />"
html_message += self.messages['event_participants_attend'];
# Loop through fetched participants
html_message += "<ul>"
for member in self.adm.events[id_event].getAllAttend():
html_message += "<li>"
html_message += member[1].username
html_message += "</li>"
html_message += "</ul>"
# Maybe
html_message += self.messages['event_participants_maybe'];
# Loop through fetched participants
html_message += "<ul>"
for member in self.adm.events[id_event].getAllMaybe():
html_message += "<li>"
html_message += member[1].username
html_message += "</li>"
html_message += "</ul>"
# Not attend
html_message += self.messages['event_participants_not_attend'];
# Loop through fetched participants
html_message += "<ul>"
for member in self.adm.events[id_event].getAllNotAttend():
html_message += "<li>"
html_message += member[1].username
html_message += "</li>"
html_message += "</ul>"
room.send_html(html_message)
except KeyError:
room.send_html(self.messages['unknown_event'])
def chat_callback(self, room, event):
room.send_text("Chat")
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()