Compare commits
No commits in common. 'f947f07874d4bd8ee41407bc5944fd4258aac75f' and '6331f7ca6ae6867b6f4174ab78d8e33fd4981b83' have entirely different histories.
f947f07874
...
6331f7ca6a
@ -0,0 +1,39 @@
|
|||||||
|
# This workflow will install Python dependencies, then run various linting programs on a single Python version
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||||
|
|
||||||
|
name: Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python 3.9
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.9
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -U isort==5.6.4 flake8==3.8.4 flake8-comprehensions==3.3.1 black==20.8b1
|
||||||
|
|
||||||
|
- name: Check import statement sorting
|
||||||
|
run: |
|
||||||
|
isort -c --df molly/ molly tests
|
||||||
|
|
||||||
|
- name: Python syntax errors, undefined names, etc.
|
||||||
|
run: |
|
||||||
|
flake8 . --count --show-source --statistics
|
||||||
|
|
||||||
|
- name: PEP8 formatting
|
||||||
|
run: |
|
||||||
|
black --check --diff molly/ molly tests
|
@ -0,0 +1,57 @@
|
|||||||
|
# This workflow will install Python dependencies, then run unit testing across the earliest and latest supported Python versions
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||||
|
|
||||||
|
name: Run unit tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
python_36:
|
||||||
|
|
||||||
|
# We need to use 20.04 to get access to the libolm3 package
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python 3.6
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.6
|
||||||
|
|
||||||
|
- name: Install project dependencies
|
||||||
|
run: |
|
||||||
|
# Install libolm, required for end-to-end encryption functionality
|
||||||
|
sudo apt install -y libolm-dev libolm3
|
||||||
|
# Install python dependencies
|
||||||
|
python setup.py install
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: |
|
||||||
|
python -m unittest
|
||||||
|
|
||||||
|
python_39:
|
||||||
|
|
||||||
|
# We need to use 20.04 to get access to the libolm3 package
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python 3.9
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.9
|
||||||
|
|
||||||
|
- name: Install project dependencies
|
||||||
|
run: |
|
||||||
|
# Install libolm, required for end-to-end encryption functionality
|
||||||
|
sudo apt install -y libolm-dev libolm3
|
||||||
|
# Install python dependencies
|
||||||
|
python setup.py install
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: |
|
||||||
|
python -m unittest
|
@ -0,0 +1,96 @@
|
|||||||
|
# Contributing to nio-template
|
||||||
|
|
||||||
|
Thank you for taking interest in this little project. Below is some information
|
||||||
|
to help you with contributing.
|
||||||
|
|
||||||
|
## Setting up your development environment
|
||||||
|
|
||||||
|
See the
|
||||||
|
[Install the dependencies section of SETUP.md](SETUP.md#install-the-dependencies)
|
||||||
|
for help setting up a running environment for the bot.
|
||||||
|
|
||||||
|
If you would rather not or are unable to run docker, the following instructions
|
||||||
|
will explain how to install the project dependencies natively.
|
||||||
|
|
||||||
|
#### Install libolm
|
||||||
|
|
||||||
|
You can install [libolm](https://gitlab.matrix.org/matrix-org/olm) from source,
|
||||||
|
or alternatively, check your system's package manager. Version `3.0.0` or
|
||||||
|
greater is required.
|
||||||
|
|
||||||
|
**(Optional) postgres development headers**
|
||||||
|
|
||||||
|
By default, the bot uses SQLite as its storage backend. This is fine for a
|
||||||
|
few hundred users, but if you plan to support a much higher volume
|
||||||
|
of requests, you may consider using Postgres as a database backend instead.
|
||||||
|
|
||||||
|
If you want to use postgres as a database backend, you'll need to install
|
||||||
|
postgres development headers:
|
||||||
|
|
||||||
|
Debian/Ubuntu:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install libpq-dev libpq5
|
||||||
|
```
|
||||||
|
|
||||||
|
Arch:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo pacman -S postgresql-libs
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Install Python dependencies
|
||||||
|
|
||||||
|
Create and activate a Python 3 virtual environment:
|
||||||
|
|
||||||
|
```
|
||||||
|
virtualenv -p python3 env
|
||||||
|
source env/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
Install python dependencies:
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
|
(Optional) If you want to use postgres as a database backend, use the following
|
||||||
|
command to install postgres dependencies alongside those that are necessary:
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install ".[postgres]"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development dependencies
|
||||||
|
|
||||||
|
There are some python dependencies that are required for linting/testing etc.
|
||||||
|
You can install them with:
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -e ".[dev]"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code style
|
||||||
|
|
||||||
|
Please follow the [PEP8](https://www.python.org/dev/peps/pep-0008/) style
|
||||||
|
guidelines and format your import statements with
|
||||||
|
[isort](https://pypi.org/project/isort/).
|
||||||
|
|
||||||
|
## Linting
|
||||||
|
|
||||||
|
Run the following script to automatically format your code. This *should* make
|
||||||
|
the linting CI happy:
|
||||||
|
|
||||||
|
```
|
||||||
|
./scripts-dev/lint.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## What to work on
|
||||||
|
|
||||||
|
Take a look at the [issues
|
||||||
|
list](https://github.com/anoadragon453/nio-template/issues). What
|
||||||
|
feature would you like to see or bug do you want to be fixed?
|
||||||
|
|
||||||
|
If you would like to talk any ideas over before working on them, you can reach
|
||||||
|
me at [@andrewm:amorgan.xyz](https://matrix.to/#/@andrewm:amorgan.xyz)
|
||||||
|
on matrix.
|
@ -0,0 +1,160 @@
|
|||||||
|
# Nio Template [![Built with matrix-nio](https://img.shields.io/badge/built%20with-matrix--nio-brightgreen)](https://github.com/poljar/matrix-nio) <a href="https://matrix.to/#/#nio-template:matrix.org"><img src="https://img.shields.io/matrix/nio-template:matrix.org?color=blue&label=Join%20the%20Matrix%20Room&server_fqdn=matrix-client.matrix.org" /></a>
|
||||||
|
|
||||||
|
A template for creating bots with
|
||||||
|
[matrix-nio](https://github.com/poljar/matrix-nio). The documentation for
|
||||||
|
matrix-nio can be found
|
||||||
|
[here](https://matrix-nio.readthedocs.io/en/latest/nio.html).
|
||||||
|
|
||||||
|
This repo contains a working Matrix echo bot that can be easily extended to your needs. Detailed documentation is included as well as a step-by-step guide on basic bot building.
|
||||||
|
|
||||||
|
Features include out-of-the-box support for:
|
||||||
|
|
||||||
|
* Bot commands
|
||||||
|
* SQLite3 and Postgres database backends
|
||||||
|
* Configuration files
|
||||||
|
* Multi-level logging
|
||||||
|
* Docker
|
||||||
|
* Participation in end-to-end encrypted rooms
|
||||||
|
|
||||||
|
## Projects using nio-template
|
||||||
|
|
||||||
|
* [anoadragon453/matrix-reminder-bot](https://github.com/anoadragon453/matrix-reminder-bot
|
||||||
|
) - A matrix bot to remind you about things
|
||||||
|
* [gracchus163/hopeless](https://github.com/gracchus163/hopeless) - COREbot for the Hope2020 conference Matrix server
|
||||||
|
* [alturiak/nio-smith](https://github.com/alturiak/nio-smith) - A modular bot for @matrix-org that can be dynamically
|
||||||
|
extended by plugins
|
||||||
|
* [anoadragon453/msc-chatbot](https://github.com/anoadragon453/msc-chatbot) - A matrix bot for matrix spec proposals
|
||||||
|
* [anoadragon453/matrix-episode-bot](https://github.com/anoadragon453/matrix-episode-bot) - A matrix bot to post episode links
|
||||||
|
* [TheForcer/vision-nio](https://github.com/TheForcer/vision-nio) - A general purpose matrix chatbot
|
||||||
|
* [anoadragon453/drawing-challenge-bot](https://github.com/anoadragon453/drawing-challenge-bot) - A matrix bot to
|
||||||
|
post historical, weekly art challenges from reddit to a room
|
||||||
|
* [8go/matrix-eno-bot](https://github.com/8go/matrix-eno-bot) - A bot to be used as a) personal assistant or b) as
|
||||||
|
an admin tool to maintain your Matrix installation or server
|
||||||
|
* [elokapina/bubo](https://github.com/elokapina/bubo) - Matrix bot to help with community management
|
||||||
|
* [elokapina/middleman](https://github.com/elokapina/middleman) - Matrix bot to act as a middleman, for example as a support bot
|
||||||
|
* [chc4/matrix-pinbot](https://github.com/chc4/matrix-pinbot) - Matrix bot for pinning messages to a dedicated channel
|
||||||
|
|
||||||
|
Want your project listed here? [Edit this
|
||||||
|
page!](https://github.com/anoadragon453/nio-template/edit/master/README.md)
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
See [SETUP.md](SETUP.md) for how to setup and run the template project.
|
||||||
|
|
||||||
|
## Project structure
|
||||||
|
|
||||||
|
*A reference of each file included in the template repository, its purpose and
|
||||||
|
what it does.*
|
||||||
|
|
||||||
|
The majority of the code is kept inside of the `molly` folder, which
|
||||||
|
is in itself a [python package](https://docs.python.org/3/tutorial/modules.html),
|
||||||
|
the `__init__.py` file inside declaring it as such.
|
||||||
|
|
||||||
|
To run the bot, the `molly` script in the root of the codebase is
|
||||||
|
available. It will import the `main` function from the `main.py` file in the
|
||||||
|
package and run it. To properly install this script into your python environment,
|
||||||
|
run `pip install -e .` in the project's root directory.
|
||||||
|
|
||||||
|
`setup.py` contains package information (for publishing your code to
|
||||||
|
[PyPI](https://pypi.org)) and `setup.cfg` just contains some configuration
|
||||||
|
options for linting tools.
|
||||||
|
|
||||||
|
`sample.config.yaml` is a sample configuration file. People running your bot
|
||||||
|
should be advised to copy this file to `config.yaml`, then edit it according to
|
||||||
|
their needs. Be sure never to check the edited `config.yaml` into source control
|
||||||
|
since it'll likely contain sensitive details such as passwords!
|
||||||
|
|
||||||
|
Below is a detailed description of each of the source code files contained within
|
||||||
|
the `molly` directory:
|
||||||
|
|
||||||
|
### `main.py`
|
||||||
|
|
||||||
|
Initialises the config file, the bot store, and nio's AsyncClient (which is
|
||||||
|
used to retrieve and send events to a matrix homeserver). It also registering
|
||||||
|
some callbacks on the AsyncClient to tell it to call some functions when
|
||||||
|
certain events are received (such as an invite to a room, or a new message in a
|
||||||
|
room the bot is in).
|
||||||
|
|
||||||
|
It also starts the sync loop. Matrix clients "sync" with a homeserver, by
|
||||||
|
asking constantly asking for new events. Each time they do, the client gets a
|
||||||
|
sync token (stored in the `next_batch` field of the sync response). If the
|
||||||
|
client provides this token the next time it syncs (using the `since` parameter
|
||||||
|
on the `AsyncClient.sync` method), the homeserver will only return new event
|
||||||
|
*since* those specified by the given token.
|
||||||
|
|
||||||
|
This token is saved and provided again automatically by using the
|
||||||
|
`client.sync_forever(...)` method.
|
||||||
|
|
||||||
|
### `config.py`
|
||||||
|
|
||||||
|
This file reads a config file at a given path (hardcoded as `config.yaml` in
|
||||||
|
`main.py`), processes everything in it and makes the values available to the
|
||||||
|
rest of the bot's code so it knows what to do. Most of the options in the given
|
||||||
|
config file have default values, so things will continue to work even if an
|
||||||
|
option is left out of the config file. Obviously there are some config values
|
||||||
|
that are required though, like the homeserver URL, username, access token etc.
|
||||||
|
Otherwise the bot can't function.
|
||||||
|
|
||||||
|
### `storage.py`
|
||||||
|
|
||||||
|
Creates (if necessary) and connects to a SQLite3 database and provides commands
|
||||||
|
to put or retrieve data from it. Table definitions should be specified in
|
||||||
|
`_initial_setup`, and any necessary migrations should be put in
|
||||||
|
`_run_migrations`. There's currently no defined method for how migrations
|
||||||
|
should work though.
|
||||||
|
|
||||||
|
### `callbacks.py`
|
||||||
|
|
||||||
|
Holds callback methods which get run when the bot get a certain type of event
|
||||||
|
from the homserver during sync. The type and name of the method to be called
|
||||||
|
are specified in `main.py`. Currently there are two defined methods, one that
|
||||||
|
gets called when a message is sent in a room the bot is in, and another that
|
||||||
|
runs when the bot receives an invite to the room.
|
||||||
|
|
||||||
|
The message callback function, `message`, checks if the message was for the
|
||||||
|
bot, and whether it was a command. If both of those are true, the bot will
|
||||||
|
process that command.
|
||||||
|
|
||||||
|
The invite callback function, `invite`, processes the invite event and attempts
|
||||||
|
to join the room. This way, the bot will auto-join any room it is invited to.
|
||||||
|
|
||||||
|
### `bot_commands.py`
|
||||||
|
|
||||||
|
Where all the bot's commands are defined. New commands should be defined in
|
||||||
|
`process` with an associated private method. `echo` and `help` commands are
|
||||||
|
provided by default.
|
||||||
|
|
||||||
|
A `Command` object is created when a message comes in that's recognised as a
|
||||||
|
command from a user directed at the bot (either through the specified command
|
||||||
|
prefix (defined by the bot's config file), or through a private message
|
||||||
|
directly to the bot. The `process` command is then called for the bot to act on
|
||||||
|
that command.
|
||||||
|
|
||||||
|
### `message_responses.py`
|
||||||
|
|
||||||
|
Where responses to messages that are posted in a room (but not necessarily
|
||||||
|
directed at the bot) are specified. `callbacks.py` will listen for messages in
|
||||||
|
rooms the bot is in, and upon receiving one will create a new `Message` object
|
||||||
|
(which contains the message text, amongst other things) and calls `process()`
|
||||||
|
on it, which can send a message to the room as it sees fit.
|
||||||
|
|
||||||
|
A good example of this would be a Github bot that listens for people mentioning
|
||||||
|
issue numbers in chat (e.g. "We should fix #123"), and the bot sending messages
|
||||||
|
to the room immediately afterwards with the issue name and link.
|
||||||
|
|
||||||
|
### `chat_functions.py`
|
||||||
|
|
||||||
|
A separate file to hold helper methods related to messaging. Mostly just for
|
||||||
|
organisational purposes. Currently just holds `send_text_to_room`, a helper
|
||||||
|
method for sending formatted messages to a room.
|
||||||
|
|
||||||
|
### `errors.py`
|
||||||
|
|
||||||
|
Custom error types for the bot. Currently there's only one special type that's
|
||||||
|
defined for when a error is found while the config file is being processed.
|
||||||
|
|
||||||
|
## Questions?
|
||||||
|
|
||||||
|
Any questions? Please ask them in
|
||||||
|
[#nio-template:amorgan.xyz](https://matrix.to/#/!vmWBOsOkoOtVHMzZgN:amorgan.xyz?via=amorgan.xyz)
|
||||||
|
and we'll help you out!
|
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Runs linting scripts over the local checkout
|
||||||
|
# isort - sorts import statements
|
||||||
|
# flake8 - lints and finds mistakes
|
||||||
|
# black - opinionated code formatter
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ $# -ge 1 ]
|
||||||
|
then
|
||||||
|
files=$*
|
||||||
|
else
|
||||||
|
files="molly molly tests"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Linting these locations: $files"
|
||||||
|
isort $files
|
||||||
|
flake8 $files
|
||||||
|
python3 -m black $files
|
@ -0,0 +1,64 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
# Check that regex-rename is installed
|
||||||
|
if ! command -v regex-rename &> /dev/null
|
||||||
|
then
|
||||||
|
echo "regex-rename python module not found. Please run 'python -m pip install regex-rename'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# GNU sed and BSD(Mac) sed handle -i differently :(
|
||||||
|
function is_gnu_sed(){
|
||||||
|
sed --version >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Allow specifying either:
|
||||||
|
# * One argument, which is the new project name, assuming the old project name is "my project name"
|
||||||
|
# * Or two arguments, where one can specify 1. the old project name and 2. the new project name
|
||||||
|
if [ $# -eq 1 ]; then
|
||||||
|
PLACEHOLDER="my project name"
|
||||||
|
REPLACEMENT=$1
|
||||||
|
elif [ $# -eq 2 ]; then
|
||||||
|
PLACEHOLDER=$1
|
||||||
|
REPLACEMENT=$2
|
||||||
|
else
|
||||||
|
echo "Usage:"
|
||||||
|
echo "./"$(basename "$0") "\"new name\""
|
||||||
|
echo "./"$(basename "$0") "\"old name\" \"new name\""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PLACEHOLDER_DASHES="${PLACEHOLDER// /-}"
|
||||||
|
PLACEHOLDER_UNDERSCORES="${PLACEHOLDER// /_}"
|
||||||
|
|
||||||
|
REPLACEMENT_DASHES="${REPLACEMENT// /-}"
|
||||||
|
REPLACEMENT_UNDERSCORES="${REPLACEMENT// /_}"
|
||||||
|
|
||||||
|
echo "Updating file and folder names..."
|
||||||
|
|
||||||
|
# Iterate over all directories (besides venv's and .git) and rename files/folders
|
||||||
|
# Yes this looks like some crazy voodoo, but it's necessary as regex-rename does
|
||||||
|
# not provide any sort of recursive functionality...
|
||||||
|
find . -type d -not -path "./env*" -not -path "./.git" -not -path "./.git*" \
|
||||||
|
-exec sh -c "cd {} && \
|
||||||
|
regex-rename --rename \"(.*)$PLACEHOLDER_DASHES(.*)\" \"\1$REPLACEMENT_DASHES\2\" && \
|
||||||
|
regex-rename --rename \"(.*)$PLACEHOLDER_UNDERSCORES(.*)\" \"\1$REPLACEMENT_UNDERSCORES\2\"" \; > /dev/null
|
||||||
|
|
||||||
|
echo "Updating references within files..."
|
||||||
|
|
||||||
|
# Iterate through each file and replace strings within files
|
||||||
|
for file in $(grep --exclude-dir=env --exclude-dir=venv --exclude-dir=.git --exclude *.pyc -lEw "$PLACEHOLDER_DASHES|$PLACEHOLDER_UNDERSCORES" -R * .[^.]*); do
|
||||||
|
echo "Checking $file"
|
||||||
|
if [[ $file != $(basename "$0") ]]; then
|
||||||
|
if is_gnu_sed; then
|
||||||
|
sed -i "s/$PLACEHOLDER_DASHES/$REPLACEMENT_DASHES/g" $file
|
||||||
|
sed -i "s/$PLACEHOLDER_UNDERSCORES/$REPLACEMENT_UNDERSCORES/g" $file
|
||||||
|
else
|
||||||
|
sed -i "" "s/$PLACEHOLDER_DASHES/$REPLACEMENT_DASHES/g" $file
|
||||||
|
sed -i "" "s/$PLACEHOLDER_UNDERSCORES/$REPLACEMENT_UNDERSCORES/g" $file
|
||||||
|
fi
|
||||||
|
echo " - $file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Done!"
|
Loading…
Reference in new issue