Added list event function

Also refactored the code a little bit more and updated the README file
according.
This commit is contained in:
Dennis Potter 2019-01-06 16:01:01 +01:00
parent 0c78434919
commit 9db6136e86
4 changed files with 150 additions and 28 deletions

View File

@ -27,15 +27,17 @@ To interact with rooms, the [Matrix Python SDK](http://matrix-org.github.io/matr
#### Mandatory files #### 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`. 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.<language>.json` with all messages should exist.
``` ```
. .
└── plugins └── plugins
├── <plugin1> ├── <plugin1>
│ ├── __init__.py │ ├── __init__.py
│ ├── <plugin1>.py │ ├── <plugin1>.py
│ ├── README.md
│ ├── config.json │ ├── config.json
│ └── messages │ └── messages
│ ├── messages.<language>.json
│ └── help │ └── help
├── <plugin2> ├── <plugin2>
@ -80,6 +82,8 @@ The code below shows the template for a simple plugin with a few features. The n
```python ```python
CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.json') CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.json')
HELP_LOCATION = os.path.join(os.path.dirname(__file__), 'help') 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: class Plugin:
""" Description of event plugin """ """ Description of event plugin """
@ -89,9 +93,17 @@ class Plugin:
with open(CONFIG_LOCATION) as json_data: with open(CONFIG_LOCATION) as json_data:
self.config = json.load(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 # Define sensitivity
self.handler = MRegexHandler("Peter <plugin1> <feature1>", self.callback1) self.handler = []
self.handler = MRegexHandler("Peter <plugin1> <feature2>", self.callback2)
self.handler.append(MRegexHandler("Peter <plugin1> <feature1>",
self.callback1))
self.handler.append(MRegexHandler("Peter <plugin1> <feature2>",
self.callback2))
# Save parent bot # Save parent bot
self.bot = bot self.bot = bot

View File

@ -164,11 +164,17 @@ class MatrixBotAPI:
self.send_message_private_public(room, event, help_text) self.send_message_private_public(room, event, help_text)
def add_handler(self, handler): 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): def handle_message(self, room, event):
# Make sure we didn't send this message # Make sure we didn't send this message
if re.match("@" + self.username, event['sender']): if re.match(self.username, event['sender']):
return return
# Loop through all installed handlers and see if they need to be called # Loop through all installed handlers and see if they need to be called

View File

@ -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 os
import json import json
import time import time
@ -9,8 +17,9 @@ import MySQLdb as mysql
from matrix_bot_api.mregex_handler import MRegexHandler from matrix_bot_api.mregex_handler import MRegexHandler
CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.json') CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.json')
HELP_LOCATION = os.path.join(os.path.dirname(__file__), 'help') HELP_LOCATION = os.path.join(os.path.dirname(__file__), 'messages/help')
NEW_EVENT_LOCATION = os.path.join(os.path.dirname(__file__), 'new_event') MESSAGES_LOCATION = os.path.join(os.path.dirname(__file__),
'messages/messages.dutch.json')
class Plugin: class Plugin:
""" Description of event plugin """ """ Description of event plugin """
@ -20,30 +29,47 @@ class Plugin:
with open(CONFIG_LOCATION) as json_data: with open(CONFIG_LOCATION) as json_data:
self.config = json.load(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 # 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 # Save parent bot
self.bot = bot self.bot = bot
# Run one time setup
self.setup()
# Start thread to check events # Start thread to check events
self.event_thread = threading.Thread(target=self.check_new_event_thread) self.event_thread = threading.Thread(target=self.check_new_event_thread)
self.event_thread.start() self.event_thread.start()
self.setup()
def setup(self): def setup(self):
"""This function only runs once, ever. It initializes the necessary """This function only runs once, ever. It initializes the necessary
SQLite tables.""" SQLite tables."""
try: try:
# Try to make a new directory
os.mkdir("data/events")
# Define query to INSERT event table to SQLite DB # Define query to INSERT event table to SQLite DB
sql = """CREATE TABLE 'events' ( sql = """CREATE TABLE 'events' (
'dat_id' integer, 'dat_id' integer,
'datetime_posted' datetime);""" 'datetime_posted' datetime);"""
# Open, execute, commit, and close SQLite database # 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 = sqlite_db.cursor()
sqlite_cursor.execute(sql) sqlite_cursor.execute(sql)
sqlite_db.commit() sqlite_db.commit()
@ -52,6 +78,17 @@ class Plugin:
except sqlite3.OperationalError: except sqlite3.OperationalError:
# Table already exists, do nothing # Table already exists, do nothing
pass 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): def check_new_event_thread(self):
while not self.bot.cancel: while not self.bot.cancel:
@ -67,12 +104,7 @@ class Plugin:
saved in an SQLite database. saved in an SQLite database.
""" """
# Connect to database mysql_db = self.connect_database()
mysql_db = mysql.connect(
user = self.config['database_credentials']['username'],
password = self.config['database_credentials']['password'],
database = self.config['database_credentials']['database'])
mysql_cursor = mysql_db.cursor() mysql_cursor = mysql_db.cursor()
# Grab all events from database that start in the future # Grab all events from database that start in the future
@ -87,10 +119,8 @@ class Plugin:
# Fetch events # Fetch events
results = mysql_cursor.fetchall() results = mysql_cursor.fetchall()
mysql_db.close()
# Open SQLite database # Open SQLite database
sqlite_db = sqlite3.connect('data/bot.db') sqlite_db = sqlite3.connect('data/events/data.db')
sqlite_cursor = sqlite_db.cursor() sqlite_cursor = sqlite_db.cursor()
@ -128,8 +158,6 @@ class Plugin:
maybe_link = function_link.format(base_date_url, '7', row[0]) maybe_link = function_link.format(base_date_url, '7', row[0])
cancel_link = function_link.format(base_date_url, '4', 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 # First, write to SQLite database that it is processed
sqlite_cursor.execute( sqlite_cursor.execute(
insert_sql, { insert_sql, {
@ -146,7 +174,7 @@ class Plugin:
for i, room in self.bot.client.get_rooms().items(): for i, room in self.bot.client.get_rooms().items():
if room.display_name in mapping[1]: if room.display_name in mapping[1]:
room.send_html( room.send_html(
new_event.format( self.messages["new_event"].format(
row[3].strftime("%d-%m-%Y"), row[3].strftime("%d-%m-%Y"),
row[3].strftime("%H:%M"), row[3].strftime("%H:%M"),
row[2], row[2],
@ -159,12 +187,82 @@ class Plugin:
sqlite_db.commit() sqlite_db.commit()
sqlite_db.close() sqlite_db.close()
# Sleep # Close MySQL
#time.sleep(self.config['update_time_span'] * 60) mysql_db.close()
time.sleep(1)
def callback(self, room, event): # Sleep
room.send_text("Information ") 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 += "<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): def help(self):
return open(HELP_LOCATION, mode="r").read() return open(HELP_LOCATION, mode="r").read()

View File

@ -0,0 +1,6 @@
{
"new_event": "<strong>@room er is zojuist een nieuw evenement aangemaakt! Op {} om {} zal \"{}\" plaatsvinden.</strong><br /><br />Ben je aangemeld op de leden database in je favoriete browser? Klik dan hier:<ul><li>&nbsp;<a href='{}'>om het evenement te bekijken</a> 👀<li>&nbsp;<a href='{}'>om je op aanwezig te zetten</a> ✅<li>&nbsp;<a href='{}'>om je op misschien te zetten</a> 🤷<li>&nbsp;<a href='{}'>om je op afwezig te zetten</a> ⛔</ul>",
"list_events_head": "De aankomende {} evenementen zijn:",
"list_event_item": "{} om {}: <strong>{}</strong> (<a href='{}'>aanmelden</a>)",
"list_event_num_err": "Let op: alleen de getallen [1,100] zijn toegestaan! Ik pak de standaard waarde 10."
}