Boilerplate
All checks were successful
git.cronocide.net/bluebubbles-bot/pipeline/head This commit looks good
All checks were successful
git.cronocide.net/bluebubbles-bot/pipeline/head This commit looks good
This commit is contained in:
parent
2e534fb7aa
commit
4b1962b49a
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@ -16,7 +16,7 @@ pipeline {
|
|||||||
}
|
}
|
||||||
environment {
|
environment {
|
||||||
WORKSPACE_PATH = "/opt/nomad/alloc/${NOMAD_ALLOC_ID}/${NOMAD_TASK_NAME}${WORKSPACE}"
|
WORKSPACE_PATH = "/opt/nomad/alloc/${NOMAD_ALLOC_ID}/${NOMAD_TASK_NAME}${WORKSPACE}"
|
||||||
DESCRIPTION = "Another amazing piece of software written by Cronocide."
|
DESCRIPTION = "A chatbot for a local BlueBlubbles server."
|
||||||
}
|
}
|
||||||
stages {
|
stages {
|
||||||
stage('Prepare') {
|
stage('Prepare') {
|
||||||
|
27
README.md
27
README.md
@ -1,12 +1,23 @@
|
|||||||
# python-template
|
# bluebubbles_bot
|
||||||
|
## A chatbot for a local BlueBlubbles server.
|
||||||
|
|
||||||
Template repository for Docker image creation and deployment.
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
# Deployment Checklist
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Justification
|
||||||
|
|
||||||
* Add Jenkins user to project as a Developer in Git
|
|
||||||
* Write the description in the Jenkinsfile env variable
|
|
||||||
* Add a private Github push mirror
|
|
||||||
* Rename the project in the README
|
|
||||||
* Delete this checklist from the README
|
|
||||||
|
124
bin/bluebubbles_bot
Normal file
124
bin/bluebubbles_bot
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#!python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
import logging
|
||||||
|
import argparse
|
||||||
|
import bluebubbles_bot
|
||||||
|
import inspect
|
||||||
|
import importlib
|
||||||
|
# Import your custom libraries here
|
||||||
|
|
||||||
|
_plugin_map = {}
|
||||||
|
_delegate_map = {}
|
||||||
|
_initialized_plugins = []
|
||||||
|
|
||||||
|
# Plugin loading code
|
||||||
|
plugin_class = 'bluebubbles_bot'.strip('-').capitalize() + 'Plugin'
|
||||||
|
def add_plugin(plugin,reload) :
|
||||||
|
"""Adds a given plugin and instance, reinitializing one if it already exists and such is specified."""
|
||||||
|
plugin_name = plugin.__module__.split('.')[-1]
|
||||||
|
if not reload and plugin_name in _plugin_map.keys():
|
||||||
|
pass
|
||||||
|
else :
|
||||||
|
# We can't startup the plugin here because it hasn't been configured. We'll handle that at runtime.
|
||||||
|
try:
|
||||||
|
# Remove any intialized objects of the same name, forcing a reinitialization
|
||||||
|
_initialized_plugins.remove(_plugin_map[plugin_name])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
_plugin_map.update({plugin_name:plugin})
|
||||||
|
|
||||||
|
def use_plugins(plugins,reload=False) :
|
||||||
|
"""Defines plugins that should be used in a lookup, optionally forcing them to reload."""
|
||||||
|
# Verify data
|
||||||
|
if type(plugins) != list :
|
||||||
|
raise ValueError('argument \'plugins\' should be of type list')
|
||||||
|
for plugin in plugins :
|
||||||
|
# Check if the plugin is a string or a descendent of a bluebubbles_bot-plugin class
|
||||||
|
if type(plugin) != str and plugin_class not in [x.__name__ for x in inspect.getmro(plugin)] :
|
||||||
|
raise ValueError('unkown type for plugin')
|
||||||
|
# Find plugins by name using a default path
|
||||||
|
if type(plugin) == str :
|
||||||
|
available_plugins = [y for x,y in search_plugins().items() if x == plugin and plugin_class in [z.__name__ for z in inspect.getmro(y)]]
|
||||||
|
if len(available_plugins) == 0 :
|
||||||
|
raise FileNotFoundError(plugin + '.py not found')
|
||||||
|
plugin = available_plugins[0]
|
||||||
|
if plugin_class in [x.__name__ for x in inspect.getmro(plugin)] :
|
||||||
|
add_plugin(plugin,reload)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def get_plugins() :
|
||||||
|
"""Returns a map of plugins configured and loaded."""
|
||||||
|
return _plugin_map
|
||||||
|
|
||||||
|
def search_plugins(directory=None) :
|
||||||
|
"""Searches a given directory for compatible plugins and returns a map of available plugin names and classes."""
|
||||||
|
if not directory :
|
||||||
|
directory = '/'.join(os.path.realpath(__file__).split('/')[:-1]) + '/' + 'plugins'
|
||||||
|
directory = os.path.normpath(os.path.expanduser(os.path.expandvars(directory)))
|
||||||
|
name_map = {}
|
||||||
|
candidates = {x.split('.')[0]:x for x in os.listdir(directory) if x.endswith('.py')}
|
||||||
|
for name,filename in candidates.items() :
|
||||||
|
try :
|
||||||
|
spec = importlib.util.spec_from_file_location(name, directory + '/' + filename)
|
||||||
|
mod = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(mod)
|
||||||
|
instance = getattr(mod,plugin_class)
|
||||||
|
name_map.update({filename.split('.')[0]:instance})
|
||||||
|
except Exception as e :
|
||||||
|
# Handle plugin loading issues if desired
|
||||||
|
print("Unable to load plugin from " + filename + ": " + str(e))
|
||||||
|
return name_map
|
||||||
|
|
||||||
|
class LoggingFormatter(logging.Formatter):
|
||||||
|
def format(self, record):
|
||||||
|
module_max_width = 30
|
||||||
|
datefmt='%Y/%m/%d/ %H:%M:%S'
|
||||||
|
level = f'[{record.levelname}]'.ljust(9)
|
||||||
|
if 'log_module' not in dir(record) :
|
||||||
|
modname = str(record.module)+'.'+str(record.name)
|
||||||
|
else :
|
||||||
|
modname = record.log_module
|
||||||
|
modname = (f'{modname}'[:module_max_width-1] + ']').ljust(module_max_width)
|
||||||
|
final = "%-7s %s [%s %s" % (self.formatTime(record, self.datefmt), level, modname, record.getMessage())
|
||||||
|
return final
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
# Command-line client
|
||||||
|
# Define constants
|
||||||
|
config_template = {'bluebubbles_bot': {}}
|
||||||
|
|
||||||
|
# Gather Argument options
|
||||||
|
EXAMPLE_TEXT='Example:\n\tbluebubbles_bot -h'
|
||||||
|
parser = argparse.ArgumentParser(epilog=EXAMPLE_TEXT,formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
parser.add_argument('-H', '--hosts', action='append', default=None, help='Collects arguments in an array.')
|
||||||
|
parser.add_argument('-d', '--dry-run', action='store_true', help='Store the existence of a variable.')
|
||||||
|
parser.add_argument('-l', '--log', action='store', help='Specify a file to log to.')
|
||||||
|
parser.add_argument('-v', '--verbose', action='count', help='Include verbose information in the output. Add \'v\'s for more output.',default=0)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
log = logging.LoggerAdapter(log,{'log_module':'bluebubbles_bot'})
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
log_options = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
|
||||||
|
if not args.verbose :
|
||||||
|
args.verbose = 0
|
||||||
|
if args.verbose > 3 :
|
||||||
|
args.verbose = 3
|
||||||
|
if args.log :
|
||||||
|
logging.basicConfig(level=log_options[args.verbose],filename=args.log)
|
||||||
|
logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
|
||||||
|
else :
|
||||||
|
logging.basicConfig(level=log_options[args.verbose])
|
||||||
|
logging.getLogger().handlers[0].setFormatter(LoggingFormatter())
|
||||||
|
logging.propagate=True
|
||||||
|
|
||||||
|
# Load plugins
|
||||||
|
available_plugins = search_plugins(directory='/'.join(os.path.realpath(__file__).split('/')[:-2]) + '/bluebubbles_bot/' + 'plugins')
|
||||||
|
use_plugins([x for x in available_plugins.values()])
|
||||||
|
# Main functions
|
||||||
|
print('Hello World!')
|
1
bluebubbles_bot/__init__.py
Normal file
1
bluebubbles_bot/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from bluebubbles_bot.bluebubbles_bot import *
|
1
bluebubbles_bot/bluebubbles_bot.py
Normal file
1
bluebubbles_bot/bluebubbles_bot.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Write your modular code (classes, functions, etc) here. They'll be automatically imported in bin/bluebubbles_bot
|
0
bluebubbles_bot/plugins/plugin.py
Normal file
0
bluebubbles_bot/plugins/plugin.py
Normal file
62
setup.py
Normal file
62
setup.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
from setuptools import setup, find_packages
|
||||||
|
from setuptools.command.install_scripts import install_scripts
|
||||||
|
from setuptools.command.install import install
|
||||||
|
from setuptools.command.develop import develop
|
||||||
|
from setuptools.command.egg_info import egg_info
|
||||||
|
from setuptools.command.build_ext import build_ext
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
|
|
||||||
|
# From https://stackoverflow.com/questions/5932804/set-file-permissions-in-setup-py-file
|
||||||
|
# https://blog.niteo.co/setuptools-run-custom-code-in-setup-py/
|
||||||
|
def customize(command) :
|
||||||
|
command_name = str(command.mro()[1].__name__).strip()
|
||||||
|
original_run = command.run
|
||||||
|
def run(self) :
|
||||||
|
# Run the rest of the installer first
|
||||||
|
original_run(self)
|
||||||
|
# Create a new subprocess to run the included shell script
|
||||||
|
print("Running " + command_name + " commands...")
|
||||||
|
current_dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
create_service_script_path = os.path.join(current_dir_path, 'setup.sh')
|
||||||
|
# stdout and stderr are combined in shell output
|
||||||
|
output=subprocess.run([create_service_script_path,command_name],stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout
|
||||||
|
print(output.decode('UTF-8'))
|
||||||
|
command.run = run
|
||||||
|
return command
|
||||||
|
|
||||||
|
@customize
|
||||||
|
class CustomInstallCommand(install) :
|
||||||
|
pass
|
||||||
|
|
||||||
|
@customize
|
||||||
|
class CustomDevelopCommand(develop) :
|
||||||
|
pass
|
||||||
|
|
||||||
|
@customize
|
||||||
|
class CustomEggInfoCommand(egg_info) :
|
||||||
|
pass
|
||||||
|
|
||||||
|
@customize
|
||||||
|
class CustomBuildExtCommand(build_ext) :
|
||||||
|
pass
|
||||||
|
|
||||||
|
files = glob.glob('bluebubbles_bot/plugins/*.py')
|
||||||
|
|
||||||
|
setup(name='bluebubbles_bot',
|
||||||
|
version='1.0.0',
|
||||||
|
url='',
|
||||||
|
license='Apache2',
|
||||||
|
author='Daniel Dayley',
|
||||||
|
author_email='github@cronocide.com',
|
||||||
|
description='A chatbot for a local BlueBlubbles server.',
|
||||||
|
packages=find_packages(exclude=['tests']),
|
||||||
|
package_data={"": ['plugins/*.py']},
|
||||||
|
install_requires=['pyyaml',],
|
||||||
|
scripts=['bin/bluebubbles_bot'],
|
||||||
|
long_description=open('README.md').read(),
|
||||||
|
|
||||||
|
zip_safe=True
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user