Transitioned from JSON to Hjson. Closes #3

All configuration files and all messages files should now preferrable be
done in Hjson. This format supports, i.a. features, multi line strings
and commenting. This greatly improves readability.

All installation and documentation files are also updated in this
commit.
This commit is contained in:
Dennis Potter 2019-01-14 23:47:38 +01:00
parent 8e746674c4
commit eb2b887c0a
17 changed files with 125 additions and 64 deletions

6
.gitignore vendored
View File

@ -1,7 +1,13 @@
# Config # Config
config.json config.json
config.hjson
*.db *.db
# Generated directories
data/
bin/
include/
# ---> Python # ---> Python
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/

View File

@ -24,7 +24,7 @@ In order for the bot framework to work, Python 3, [virtualenv](https://virtualen
### Automatic installation (Linux) ### Automatic installation (Linux)
On Linux, the bash script `install.sh` can be used to install the bot. On Linux, the bash script `install.sh` can be used to install the bot.
``` ```
install.sh : Creates a virtualenv, an initial config.json, and installs install.sh : Creates a virtualenv, an initial config.hjson, and installs
all required packages all required packages
install.sh service: Runs ./install and additionally adds a systemd service. install.sh service: Runs ./install and additionally adds a systemd service.
This command will ask you for root credentials! This command will ask you for root credentials!
@ -43,7 +43,7 @@ and install all required packages
``` ```
pip3 install -r requirements.txt pip3 install -r requirements.txt
``` ```
Now, you need to create a configuration file `config.json`. The template `config.json.template` can be used for this purpose. Now, you need to create a configuration file `config.hjson`. The template `config.hjson.template` can be used for this purpose.
Finally, the bot can be started by executing the following command within the virtual environment: Finally, the bot can be started by executing the following command within the virtual environment:
@ -52,7 +52,7 @@ Finally, the bot can be started by executing the following command within the vi
``` ```
## Plugins ## Plugins
The bot has different plugins that can be activated by sending `[Keyword in bot's config.json] [Keyword(s) in plugin's config.json]` to a room in which the bot is present. Below, under [List of available plugins](list-of-available-plugins), you can first find a list of already available plugins. Then, under [API](#api), you can find a description on how to develop a new plugin. The bot has different plugins that can be activated by sending `[Keyword in bot's config.hjson] [Keyword(s) in plugin's config.hjson]` to a room in which the bot is present. Below, under [List of available plugins](list-of-available-plugins), you can first find a list of already available plugins. Then, under [API](#api), you can find a description on how to develop a new plugin.
### List of available plugins: ### List of available plugins:
* [Hello plugin](src/branch/master/plugins/events): An example plugin that interacts with users that send "Hello bot" to the bot. * [Hello plugin](src/branch/master/plugins/events): An example plugin that interacts with users that send "Hello bot" to the bot.
@ -63,9 +63,9 @@ The bot has different plugins that can be activated by sending `[Keyword in bot'
To interact with rooms, the [Matrix Python SDK](http://matrix-org.github.io/matrix-python-sdk/<Paste>) can be used. To interact with rooms, the [Matrix Python SDK](http://matrix-org.github.io/matrix-python-sdk/<Paste>) can be used.
#### Mandatory/recommended files #### Mandatory/recommended files
To create a plugin, a few files must or can be present. The tree below shows the general structure of a plugin. A new plugin must be placed in a directory that has the name of that plugin. The main class (more on that later) of the plugin must be defined in a Python file with that same name. The plugin may not use bot's main configuration file. All configuration must be placed in a separate `config.json`. To create a plugin, a few files must or can be present. The tree below shows the general structure of a plugin. A new plugin must be placed in a directory that has the name of that plugin. The main class (more on that later) of the plugin must be defined in a Python file with that same name. The plugin may not use bot's main configuration file. All configuration must be placed in a separate `config.hjson`. More information on Hjson can be found on [their website](https://hjson.org).
A subdirectory `messages` is used to store messages. Within this directory, a file `help` may 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. A subdirectory `messages` is used to store messages. Within this directory, a file `help` may 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>.hjson` with all messages should exist.
The optional file `requirements.txt` is used to define all dependencies. The framework will automatically install them before the first time the plugin is used. This happens even *before* the `setup()` function is invoked (more on that later). The optional file `requirements.txt` is used to define all dependencies. The framework will automatically install them before the first time the plugin is used. This happens even *before* the `setup()` function is invoked (more on that later).
@ -77,10 +77,10 @@ Although recommended, the help file is also not mandatory. If no help message fo
│ ├── __init__.py │ ├── __init__.py
│ ├── <plugin1>.py │ ├── <plugin1>.py
│ ├── README.md │ ├── README.md
│ ├── config.json │ ├── config.hjson
│ ├── requirements.txt │ ├── requirements.txt
│ └── messages │ └── messages
│ ├── messages.<language>.json │ ├── messages.<language>.hjson
│ └── help │ └── help
├── <plugin2> ├── <plugin2>
@ -126,22 +126,22 @@ The code below shows the template for a simple plugin with a few features. The n
* The Plugin class must contain a method `help()` which only returns the content of `plugin1/messages/help` * The Plugin class must contain a method `help()` which only returns the content of `plugin1/messages/help`
```python ```python
CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.json') CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.hjson')
HELP_LOCATION = os.path.join(os.path.dirname(__file__), 'help') MESSAGES_DIR = os.path.join(os.path.dirname(__file__), 'messages')
MESSAGES_LOCATION = os.path.join(os.path.dirname(__file__), HELP_LOCATION = MESSAGES_DIR + '/help'
'messages/messages.dutch.json') MESSAGES_LOCATION = MESSAGES_DIR + /messages.dutch.hjson'
class Plugin: class Plugin:
""" Description of event plugin """ """ Description of event plugin """
def __init__(self, bot): def __init__(self, bot):
# Load the configuration # Load the configuration
with open(CONFIG_LOCATION) as json_data: with open(CONFIG_LOCATION) as hjson_data:
self.config = json.load(json_data) self.config = hjson.load(hjson_data)
# Load all messages for this plugin # Load all messages for this plugin
with open(MESSAGES_LOCATION) as json_data: with open(MESSAGES_LOCATION) as hjson_data:
self.messages = json.load(json_data) self.messages = hjson.load(hjson_data)
# Define sensitivity # Define sensitivity
self.handler = [] self.handler = []

View File

@ -24,12 +24,12 @@
# This script does the following: # This script does the following:
# - create virtualenv # - create virtualenv
# - install requirements of main bot # - install requirements of main bot
# - create initial config.json # - create initial config.hjson
# - (optional) create systemd service # - (optional) create systemd service
if [[ $1 == help ]]; then if [[ $1 == help ]]; then
echo "Help:" echo "Help:"
echo "install.sh : Creates a virtualenv, an initial config.json, and installs" echo "install.sh : Creates a virtualenv, an initial config.hjson, and installs"
echo " all required packages" echo " all required packages"
echo "install.sh service: Runs ./install and additionally adds a systemd service." echo "install.sh service: Runs ./install and additionally adds a systemd service."
echo " This command will ask you for root credentials!" echo " This command will ask you for root credentials!"
@ -79,7 +79,7 @@ availability pip3
pip3 install -r requirements.txt pip3 install -r requirements.txt
################################################################# #################################################################
# Create config.json # Create config.hjson
################################################################# #################################################################
echo "" echo ""
echo "############################################################################" echo "############################################################################"
@ -96,11 +96,11 @@ echo "plugins."
echo "############################################################################" echo "############################################################################"
echo "" echo ""
if [[ -f $DIR/config.json ]]; then if [[ -f $DIR/config.hjson ]]; then
read -p "$DIR/config.json already exists. Should I overwrite it? [y/n]: " ow read -p "$DIR/config.hjson already exists. Should I overwrite it? [y/n]: " ow
if [[ $ow != y ]]; then if [[ $ow != y ]]; then
echo "ERR: Cannot write to $DIR/config.json!" echo "ERR: Cannot write to $DIR/config.hjson!"
exit 1 exit 1
fi fi
fi fi
@ -114,7 +114,7 @@ sed -e "s|\${server}|${server}|"\
-e "s|\${username}|${username}|"\ -e "s|\${username}|${username}|"\
-e "s|\${password}|${password}|"\ -e "s|\${password}|${password}|"\
-e "s|\${superuser}|${superuser}|"\ -e "s|\${superuser}|${superuser}|"\
$DIR/config.json.template > $DIR/config.json $DIR/config.hjson.template > $DIR/config.hjson
################################################################# #################################################################
# Configure service # Configure service

View File

@ -2,6 +2,7 @@ import traceback
import re import re
import os import os
import sys import sys
import hjson
import importlib import importlib
import sqlite3 import sqlite3
import datetime as dt import datetime as dt
@ -14,10 +15,11 @@ from matrix_client.user import User
from matrix_bot_api.mregex_handler import MRegexHandler from matrix_bot_api.mregex_handler import MRegexHandler
HELP_LOCATION = os.path.join(os.path.dirname(__file__), 'help') MESSAGES_DIR = os.path.join(os.path.dirname(__file__), 'messages')
DATA_LOCATION = os.path.join(os.path.dirname(__file__), '../data/bot.db') DATA_LOCATION = os.path.join(os.path.dirname(__file__), '../data/bot.db')
private_message = "Hey {}! Ik heb je even een privébericht gestuurd 🙂" HELP_LOCATION = MESSAGES_DIR + '/help'
MESSAGES_LOCATION = MESSAGES_DIR + '/messages.dutch.hjson'
def eprint(*args, **kwargs): def eprint(*args, **kwargs):
"""Print error messages to stderr""" """Print error messages to stderr"""
@ -84,6 +86,11 @@ class MatrixBotAPI:
self.config['triggers']['help'], self.help) self.config['triggers']['help'], self.help)
self.add_handler(self.help_handler) self.add_handler(self.help_handler)
# Load messages
with open(MESSAGES_LOCATION) as hjson_data:
self.messages = hjson.load(hjson_data)
def add_plugins(self): def add_plugins(self):
"""Acquire list of plugins from configuration, load them, """Acquire list of plugins from configuration, load them,
initialize them, and add them to a list of object initialize them, and add them to a list of object
@ -264,7 +271,8 @@ class MatrixBotAPI:
if room != orig_room: if room != orig_room:
display_name_sender = self.api.get_display_name(event['sender']) display_name_sender = self.api.get_display_name(event['sender'])
orig_room.send_text(private_message.format(display_name_sender)) orig_room.send_text(self.messages['private_message'].format(
display_name_sender))
def help(self, room, event): def help(self, room, event):
"""Prints a general help message and then grabs all help """Prints a general help message and then grabs all help

View File

@ -0,0 +1,6 @@
{
"private_message":
'''
Hey {}! Ik heb je even een privébericht gestuurd 🙂
''',
}

View File

@ -7,7 +7,7 @@ __maintainer__ = "Dennis Potter"
__email__ = "dennis@dennispotter.eu" __email__ = "dennis@dennispotter.eu"
import os import os
import json import hjson
import time import time
import sqlite3 import sqlite3
import threading import threading
@ -16,13 +16,13 @@ import MySQLdb as mysql
from matrix_bot_api.mregex_handler import MRegexHandler from matrix_bot_api.mregex_handler import MRegexHandler
MESSAGE_DIR = os.path.join(os.path.dirname(__file__), 'messages') MESSAGES_DIR = os.path.join(os.path.dirname(__file__), 'messages')
DATA_DIR = os.path.join(os.path.dirname(__file__),'../../data/events') DATA_DIR = os.path.join(os.path.dirname(__file__),'../../data/events')
CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.json') CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.hjson')
DATA_LOCATION = DATA_DIR + '/data.db' DATA_LOCATION = DATA_DIR + '/data.db'
HELP_LOCATION = MESSAGE_DIR + '/help' HELP_LOCATION = MESSAGES_DIR + '/help'
MESSAGES_LOCATION = MESSAGE_DIR + '/messages.dutch.json' MESSAGES_LOCATION = MESSAGES_DIR + '/messages.dutch.hjson'
class Plugin: class Plugin:
""" This plugin grabs events from Admidio (https://admidio.org) """ This plugin grabs events from Admidio (https://admidio.org)
@ -30,12 +30,12 @@ class Plugin:
""" """
def __init__(self, bot): def __init__(self, bot):
# Load the configuration # Load the configuration
with open(CONFIG_LOCATION) as json_data: with open(CONFIG_LOCATION) as hjson_data:
self.config = json.load(json_data) self.config = hjson.load(hjson_data)
# Load all messages for this plugin # Load all messages for this plugin
with open(MESSAGES_LOCATION) as json_data: with open(MESSAGES_LOCATION) as hjson_data:
self.messages = json.load(json_data) self.messages = hjson.load(hjson_data)
# Define sensitivity # Define sensitivity
self.handler = [] self.handler = []

View File

@ -0,0 +1,35 @@
{
"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>
<li>&nbsp;<a href='{}'>om je op aanwezig te zetten</a> ✅</li>
<li>&nbsp;<a href='{}'>om je op misschien te zetten</a> 🤷</li>
<li>&nbsp;<a href='{}'>om je op afwezig te zetten</a> ⛔</li>
</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.
'''
}

View File

@ -1,6 +0,0 @@
{
"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."
}

View File

@ -0,0 +1,8 @@
{
"coupon_message" :
'''
De huidige code om {}% korting te krijgen is: <i><strong>{}</strong></i>.
Ga meteen naar <a href="https://alcuinus.nl/shop">de webshop</a> om
hem te gebruiken!
'''
}

View File

@ -1,5 +0,0 @@
{
"coupon_message" : "De huidige code om {}% korting te krijgen is: <i><strong>{}</strong></i>. Ga meteen naar <a href=\"https://alcuinus.nl/shop\">de webshop</a> om hem te gebruiken!"
}

View File

@ -1,17 +1,25 @@
__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"
from matrix_bot_api.mregex_handler import MRegexHandler from matrix_bot_api.mregex_handler import MRegexHandler
from woocommerce import API from woocommerce import API
from base64 import b64encode from base64 import b64encode
import os import os
import json import hjson
import datetime as dt import datetime as dt
MESSAGE_DIR = os.path.join(os.path.dirname(__file__), 'messages') MESSAGE_DIR = os.path.join(os.path.dirname(__file__), 'messages')
DATA_DIR = os.path.join(os.path.dirname(__file__),'../../data/woocommerce') DATA_DIR = os.path.join(os.path.dirname(__file__),'../../data/woocommerce')
CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.json') CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.hjson')
DATA_LOCATION = DATA_DIR + '/id.json' DATA_LOCATION = DATA_DIR + '/id.hjson'
HELP_LOCATION = MESSAGE_DIR + '/help' HELP_LOCATION = MESSAGE_DIR + '/help'
MESSAGES_LOCATION = MESSAGE_DIR + '/messages.dutch.json' MESSAGES_LOCATION = MESSAGE_DIR + '/messages.dutch.hjson'
class Plugin: class Plugin:
""" This is an example plugin with only a single callback. When """ This is an example plugin with only a single callback. When
@ -21,16 +29,16 @@ class Plugin:
def __init__(self, bot): def __init__(self, bot):
# Load the configuration # Load the configuration
with open(CONFIG_LOCATION) as json_data: with open(CONFIG_LOCATION) as hjson_data:
self.config = json.load(json_data) self.config = hjson.load(hjson_data)
# Load ID of coupon # Load ID of coupon
with open(DATA_LOCATION) as json_data: with open(DATA_LOCATION) as hjson_data:
self.coupon_id = json.load(json_data) self.coupon_id = hjson.load(hjson_data)
# Load all messages for this plugin # Load all messages for this plugin
with open(MESSAGES_LOCATION) as json_data: with open(MESSAGES_LOCATION) as hjson_data:
self.messages = json.load(json_data) self.messages = hjson.load(hjson_data)
# Define sensitivity # Define sensitivity
self.handler = [] self.handler = []
@ -91,8 +99,8 @@ def setup():
"""This function initializes a coupon with a given percentage""" """This function initializes a coupon with a given percentage"""
# Load the configuration # Load the configuration
with open(CONFIG_LOCATION) as json_data: with open(CONFIG_LOCATION) as hjson_data:
config = json.load(json_data) config = hjson.load(hjson_data)
# Create random token and determine max validity # Create random token and determine max validity
vld_days = config['coupon']['max_days'] vld_days = config['coupon']['max_days']
@ -120,12 +128,12 @@ def setup():
) )
# Send to shop # Send to shop
ret = wcapi.post("coupons", data) ret = wcapi.post("coupons", data).json()
# Create data directory # Create data directory
os.mkdir(DATA_DIR) os.mkdir(DATA_DIR)
# Write to JSON file to save ID # Write to JSON file to save ID
with open(DATA_LOCATION, 'w') as json_data: with open(DATA_LOCATION, 'w') as hjson_data:
json.dump(ret['id'], json_data) hjson.dump(ret['id'], hjson_data)

View File

@ -1 +1,2 @@
matrix-client>=0.3.2 matrix-client>=0.3.2
hjson>=3.0.1

8
run.py
View File

@ -11,7 +11,7 @@ __version__ = "0.5.0"
__maintainer__ = "Dennis Potter" __maintainer__ = "Dennis Potter"
__email__ = "dennis@dennispotter.eu" __email__ = "dennis@dennispotter.eu"
import json import hjson
import os import os
import time import time
import threading import threading
@ -19,12 +19,12 @@ import threading
# Bot API import # Bot API import
from matrix_bot_api.matrix_bot_api import MatrixBotAPI from matrix_bot_api.matrix_bot_api import MatrixBotAPI
CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.json') CONFIG_LOCATION = os.path.join(os.path.dirname(__file__), 'config.hjson')
def main(): def main():
# Load the configuration # Load the configuration
with open(CONFIG_LOCATION) as json_data: with open(CONFIG_LOCATION) as hjson_data:
config = json.load(json_data) config = hjson.load(hjson_data)
# Create an instance of the MatrixBotAPI # Create an instance of the MatrixBotAPI
bot = MatrixBotAPI(config) bot = MatrixBotAPI(config)