__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 minutes for new events in the database. Since the check will only be performed every 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 += "
" # 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 += "
" html_message += self.messages['event_participants_attend']; # Loop through fetched participants html_message += "" # Maybe html_message += self.messages['event_participants_maybe']; # Loop through fetched participants html_message += "" # Not attend html_message += self.messages['event_participants_not_attend']; # Loop through fetched participants html_message += "" 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()