diff --git a/README.md b/README.md index 6aa650a..d5586b4 100644 --- a/README.md +++ b/README.md @@ -27,15 +27,17 @@ To interact with rooms, the [Matrix Python SDK](http://matrix-org.github.io/matr #### Mandatory files To create a plugin, a few mandatory files must be present. The tree below shows the general structure of a plugin. A new plugin must be placed in a directory with the same name. The main class (more on that later) of the plugin must be defined in a Python file with the same name within that directory. The event may not use bot's main configuration file. All configuration must be placed in a separate `config.json`. -A subdirectory `messages` is used to store messages. Within this directory, a file `help` must be present. The content of this file must be returned with a method of the plugin class (more on that later). +A subdirectory `messages` is used to store messages. Within this directory, a file `help` must be present. The content of this file must be returned with a method of the plugin class (more on that later). Furthermore, for every language a file `messages..json` with all messages should exist. ``` . └── plugins ├── │ ├── __init__.py │ ├── .py + │ ├── README.md │ ├── config.json │ └── messages + │ ├── messages..json │ └── help ├── ╎ @@ -80,6 +82,8 @@ The code below shows the template for a simple plugin with a few features. The n ```python CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.json') HELP_LOCATION = os.path.join(os.path.dirname(__file__), 'help') +MESSAGES_LOCATION = os.path.join(os.path.dirname(__file__), + 'messages/messages.dutch.json') class Plugin: """ Description of event plugin """ @@ -89,9 +93,17 @@ class Plugin: with open(CONFIG_LOCATION) as json_data: self.config = json.load(json_data) + # Load all messages for this plugin + with open(MESSAGES_LOCATION) as json_data: + self.messages = json.load(json_data) + # Define sensitivity - self.handler = MRegexHandler("Peter ", self.callback1) - self.handler = MRegexHandler("Peter ", self.callback2) + self.handler = [] + + self.handler.append(MRegexHandler("Peter ", + self.callback1)) + self.handler.append(MRegexHandler("Peter ", + self.callback2)) # Save parent bot self.bot = bot diff --git a/matrix_bot_api/matrix_bot_api.py b/matrix_bot_api/matrix_bot_api.py index 853ad8a..1bec33f 100644 --- a/matrix_bot_api/matrix_bot_api.py +++ b/matrix_bot_api/matrix_bot_api.py @@ -164,11 +164,17 @@ class MatrixBotAPI: self.send_message_private_public(room, event, help_text) def add_handler(self, handler): - self.handlers.append(handler) + try: + # Assume it's a list and not a single handler + for handler_obj in handler: + self.handlers.append(handler_obj) + except TypeError: + # If it is not a list, TypeError: not iterable will occur + self.handlers.append(handler) def handle_message(self, room, event): # Make sure we didn't send this message - if re.match("@" + self.username, event['sender']): + if re.match(self.username, event['sender']): return # Loop through all installed handlers and see if they need to be called diff --git a/plugins/events/events.py b/plugins/events/events.py index 22c0340..45a58ee 100644 --- a/plugins/events/events.py +++ b/plugins/events/events.py @@ -1,3 +1,11 @@ +__author__ = "Dennis Potter" +__copyright__ = "Copyright 2019, Dennis Potter" +__credits__ = ["Dennis Potter"] +__license__ = "GPL-3.0" +__version__ = "1.0.1" +__maintainer__ = "Dennis Potter" +__email__ = "dennis@dennispotter.eu" + import os import json import time @@ -9,8 +17,9 @@ import MySQLdb as mysql from matrix_bot_api.mregex_handler import MRegexHandler CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.json') -HELP_LOCATION = os.path.join(os.path.dirname(__file__), 'help') -NEW_EVENT_LOCATION = os.path.join(os.path.dirname(__file__), 'new_event') +HELP_LOCATION = os.path.join(os.path.dirname(__file__), 'messages/help') +MESSAGES_LOCATION = os.path.join(os.path.dirname(__file__), + 'messages/messages.dutch.json') class Plugin: """ Description of event plugin """ @@ -20,30 +29,47 @@ class Plugin: with open(CONFIG_LOCATION) as json_data: self.config = json.load(json_data) + # Load all messages for this plugin + with open(MESSAGES_LOCATION) as json_data: + self.messages = json.load(json_data) + # Define sensitivity - self.handler = MRegexHandler("Peter evenementen", self.callback) + 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 + # Run one time setup + self.setup() + # Start thread to check events self.event_thread = threading.Thread(target=self.check_new_event_thread) self.event_thread.start() - self.setup() - def setup(self): """This function only runs once, ever. It initializes the necessary SQLite tables.""" try: + # Try to make a new directory + os.mkdir("data/events") + # 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/bot.db') + sqlite_db = sqlite3.connect('data/events/data.db') sqlite_cursor = sqlite_db.cursor() sqlite_cursor.execute(sql) sqlite_db.commit() @@ -52,6 +78,17 @@ class Plugin: except sqlite3.OperationalError: # Table already exists, do nothing pass + except FileExistsError: + # Directory already exists, do nothing + pass + + 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: @@ -67,12 +104,7 @@ class Plugin: saved in an SQLite database. """ - # Connect to database - mysql_db = mysql.connect( - user = self.config['database_credentials']['username'], - password = self.config['database_credentials']['password'], - database = self.config['database_credentials']['database']) - + mysql_db = self.connect_database() mysql_cursor = mysql_db.cursor() # Grab all events from database that start in the future @@ -87,10 +119,8 @@ class Plugin: # Fetch events results = mysql_cursor.fetchall() - mysql_db.close() - # Open SQLite database - sqlite_db = sqlite3.connect('data/bot.db') + sqlite_db = sqlite3.connect('data/events/data.db') sqlite_cursor = sqlite_db.cursor() @@ -128,8 +158,6 @@ class Plugin: maybe_link = function_link.format(base_date_url, '7', row[0]) cancel_link = function_link.format(base_date_url, '4', row[0]) - new_event = open(NEW_EVENT_LOCATION, mode="r").read() - # First, write to SQLite database that it is processed sqlite_cursor.execute( insert_sql, { @@ -146,7 +174,7 @@ class Plugin: for i, room in self.bot.client.get_rooms().items(): if room.display_name in mapping[1]: room.send_html( - new_event.format( + self.messages["new_event"].format( row[3].strftime("%d-%m-%Y"), row[3].strftime("%H:%M"), row[2], @@ -159,12 +187,82 @@ class Plugin: sqlite_db.commit() sqlite_db.close() - # Sleep - #time.sleep(self.config['update_time_span'] * 60) - time.sleep(1) + # Close MySQL + mysql_db.close() - def callback(self, room, event): - room.send_text("Information ") + # 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 + if 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_cursor = self.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 += "
    " + 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 += "
  • " + 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 += "
  • " + + html_message += "
" + + # 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() diff --git a/plugins/events/messages/messages.dutch.json b/plugins/events/messages/messages.dutch.json new file mode 100644 index 0000000..881cca6 --- /dev/null +++ b/plugins/events/messages/messages.dutch.json @@ -0,0 +1,6 @@ +{ + "new_event": "@room er is zojuist een nieuw evenement aangemaakt! Op {} om {} zal \"{}\" plaatsvinden.

Ben je aangemeld op de leden database in je favoriete browser? Klik dan hier:", + "list_events_head": "De aankomende {} evenementen zijn:", + "list_event_item": "{} om {}: {} (aanmelden)", + "list_event_num_err": "Let op: alleen de getallen [1,100] zijn toegestaan! Ik pak de standaard waarde 10." +}