Dennis Potter
ca86ade0fa
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.
374 lines
12 KiB
Python
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()
|
|
|