Moved from postgres to sqlite for portability and dependency reduction
This commit is contained in:
parent
57e36dd7cb
commit
eda71bd725
|
@ -1,6 +1,7 @@
|
||||||
# Project specific
|
# Project specific
|
||||||
*.kate-swp
|
*.kate-swp
|
||||||
*.log
|
*.log
|
||||||
|
data/
|
||||||
config/owncast.json
|
config/owncast.json
|
||||||
|
|
||||||
# ---> Python
|
# ---> Python
|
||||||
|
|
|
@ -8,13 +8,11 @@ Code attributions can be found in `ATTRIBUTIONS.md`
|
||||||
* [Owncast][] server
|
* [Owncast][] server
|
||||||
* Python 3
|
* Python 3
|
||||||
* Flask
|
* Flask
|
||||||
* psycopg2
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
* Install Flask and (optionally for the quote system) psycopg2
|
* Install Flask and (optionally for the quote system) psycopg2
|
||||||
* `pip --user install Flask`
|
* `pip --user install Flask`
|
||||||
* If you don't want to fiddle with psycopg2, you can install `python-psycopg2` from your distribution's package manager
|
|
||||||
* Create an Owncast webhook url pointing to your bot's location
|
* Create an Owncast webhook url pointing to your bot's location
|
||||||
* http://localhost:5000/webhook/owncast if bot and owncast are on the same machine
|
* http://localhost:5000/webhook/owncast if bot and owncast are on the same machine
|
||||||
* Copy `config-example.json` to `config.json` and fill out the information required
|
* Copy `config-example.json` to `config.json` and fill out the information required
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from ameliabot.logger import ColorizedArgsFormatter, BraceFormatStyleFormatter
|
||||||
|
from ameliabot.logger import logging
|
||||||
|
|
||||||
|
|
||||||
|
__version__ = "0.0.1"
|
||||||
|
|
||||||
|
# Set up logging
|
||||||
|
root_logger = logging.getLogger()
|
||||||
|
root_logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
console_level = "DEBUG"
|
||||||
|
console_handler = logging.StreamHandler(stream=sys.stdout)
|
||||||
|
console_handler.setLevel(console_level)
|
||||||
|
console_format = "%(asctime)s - %(levelname)-8s - %(name)-25s - %(message)s" # NOQA
|
||||||
|
colored_formatter = ColorizedArgsFormatter(console_format)
|
||||||
|
console_handler.setFormatter(colored_formatter)
|
||||||
|
root_logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
file_handler = logging.FileHandler("app.log")
|
||||||
|
file_level = "DEBUG"
|
||||||
|
file_handler.setLevel(file_level)
|
||||||
|
file_format = "%(asctime)s - %(name)s (%(lineno)s) - %(levelname)-8s - %(threadName)-12s - %(message)s" # NOQA
|
||||||
|
file_handler.setFormatter(BraceFormatStyleFormatter(file_format))
|
||||||
|
root_logger.addHandler(file_handler)
|
||||||
|
|
||||||
|
# Get basic owncast bot config
|
||||||
|
try:
|
||||||
|
with open("config/owncast.json", "r") as f:
|
||||||
|
config = json.load(f)
|
||||||
|
logging.info("Configuration loaded")
|
||||||
|
except FileNotFoundError:
|
||||||
|
logging.error("Configuration file not found.")
|
||||||
|
logging.error("Please see README.md for more information.")
|
||||||
|
raise SystemExit
|
||||||
|
|
||||||
|
with open("commands.json", "r") as f:
|
||||||
|
commands = json.load(f)
|
||||||
|
logging.info("Commands loaded")
|
||||||
|
|
||||||
|
with open("alias.json", "r") as f:
|
||||||
|
aliases = json.load(f)
|
||||||
|
logging.info("Aliases loaded")
|
|
@ -1,30 +0,0 @@
|
||||||
"""
|
|
||||||
colargulog
|
|
||||||
Python3 Logging with Colored Arguments and new string formatting style
|
|
||||||
|
|
||||||
Written by david.ohana@ibm.com
|
|
||||||
License: Apache-2.0
|
|
||||||
"""
|
|
||||||
import sys
|
|
||||||
from ameliabot.logger import ColorizedArgsFormatter, BraceFormatStyleFormatter
|
|
||||||
from ameliabot.logger import logging
|
|
||||||
|
|
||||||
|
|
||||||
def init_logging():
|
|
||||||
root_logger = logging.getLogger()
|
|
||||||
root_logger.setLevel(logging.DEBUG)
|
|
||||||
|
|
||||||
console_level = "DEBUG"
|
|
||||||
console_handler = logging.StreamHandler(stream=sys.stdout)
|
|
||||||
console_handler.setLevel(console_level)
|
|
||||||
console_format = "%(asctime)s - %(levelname)-8s - %(name)-25s - %(message)s" # NOQA
|
|
||||||
colored_formatter = ColorizedArgsFormatter(console_format)
|
|
||||||
console_handler.setFormatter(colored_formatter)
|
|
||||||
root_logger.addHandler(console_handler)
|
|
||||||
|
|
||||||
file_handler = logging.FileHandler("app.log")
|
|
||||||
file_level = "DEBUG"
|
|
||||||
file_handler.setLevel(file_level)
|
|
||||||
file_format = "%(asctime)s - %(name)s (%(lineno)s) - %(levelname)-8s - %(threadName)-12s - %(message)s" # NOQA
|
|
||||||
file_handler.setFormatter(BraceFormatStyleFormatter(file_format))
|
|
||||||
root_logger.addHandler(file_handler)
|
|
|
@ -1,41 +1,15 @@
|
||||||
import html
|
import html
|
||||||
import json
|
|
||||||
import random
|
import random
|
||||||
import requests
|
import requests
|
||||||
from requests.structures import CaseInsensitiveDict
|
from requests.structures import CaseInsensitiveDict
|
||||||
from ameliabot.init_logging import init_logging
|
from ameliabot import __version__, config, aliases, commands
|
||||||
from ameliabot.logger import logging
|
from ameliabot.logger import logging
|
||||||
from flask import request
|
from flask import request
|
||||||
|
|
||||||
init_logging()
|
|
||||||
bot_version = "0.0.1"
|
|
||||||
|
|
||||||
# Get basic owncast bot config
|
|
||||||
try:
|
|
||||||
with open("config/owncast.json", "r") as f:
|
|
||||||
config = json.load(f)
|
|
||||||
logging.info("Configuration loaded")
|
|
||||||
except FileNotFoundError:
|
|
||||||
logging.error("Configuration file not found.")
|
|
||||||
logging.error("Please see README.md for more information.")
|
|
||||||
raise SystemExit
|
|
||||||
|
|
||||||
# Make quote system and dependencies optional
|
# Make quote system and dependencies optional
|
||||||
if config["quote_enabled"]:
|
if config["quote_enabled"]:
|
||||||
from ameliabot.quote import Quote
|
from ameliabot.quote import Quote
|
||||||
quote = Quote(
|
quote = Quote()
|
||||||
database=config["quote_db_name"],
|
|
||||||
user=config["quote_db_user"],
|
|
||||||
password=config["quote_db_pass"],
|
|
||||||
)
|
|
||||||
|
|
||||||
with open("commands.json", "r") as f:
|
|
||||||
commands = json.load(f)
|
|
||||||
logging.info("Commands loaded")
|
|
||||||
|
|
||||||
with open("alias.json", "r") as f:
|
|
||||||
aliases = json.load(f)
|
|
||||||
logging.info("Aliases loaded")
|
|
||||||
|
|
||||||
# prepare the header for the bot posts
|
# prepare the header for the bot posts
|
||||||
headers = CaseInsensitiveDict()
|
headers = CaseInsensitiveDict()
|
||||||
|
@ -112,7 +86,7 @@ def process_chat(data):
|
||||||
"random": str(random.randrange(1, 100, 1)),
|
"random": str(random.randrange(1, 100, 1)),
|
||||||
"commands": ", ".join(list(commands.keys())),
|
"commands": ", ".join(list(commands.keys())),
|
||||||
"aliases": ", ".join(list(aliases.keys())),
|
"aliases": ", ".join(list(aliases.keys())),
|
||||||
"bot_version": bot_version,
|
"bot_version": __version__,
|
||||||
"quote": get_quote(first_parameter),
|
"quote": get_quote(first_parameter),
|
||||||
"quote_parameters": "Not implemented yet",
|
"quote_parameters": "Not implemented yet",
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,25 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import random
|
import random
|
||||||
|
import sqlite3
|
||||||
from ameliabot.logger import logging
|
from ameliabot.logger import logging
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
import psycopg2
|
|
||||||
except ModuleNotFoundError:
|
|
||||||
logging.error("Please install psycopg2.")
|
|
||||||
logging.error("Please see README.md for more information.")
|
|
||||||
raise SystemExit
|
|
||||||
|
|
||||||
|
|
||||||
class Quote:
|
class Quote:
|
||||||
def __init__(self, database, user, password):
|
def __init__(self):
|
||||||
self.conn = psycopg2.connect(
|
# TODO: Generalise the db connection so that other parts can use it
|
||||||
database=database, user=user, password=password)
|
self.conn = ""
|
||||||
|
self.__init_table()
|
||||||
self.num_quotes = self._get_num_quotes()
|
self.num_quotes = self._get_num_quotes()
|
||||||
|
logging.info("Quote subsystem online")
|
||||||
|
|
||||||
def insert(self, owner, submitter, text):
|
def insert(self, owner, submitter, text):
|
||||||
cur = self.conn.cursor()
|
|
||||||
text = text.replace("'", "''")
|
text = text.replace("'", "''")
|
||||||
cur.execute('''
|
self.conn.execute('''
|
||||||
INSERT INTO quotes (owner, submitter, text, timestamp)
|
INSERT INTO quotes (submitter, text, timestamp)
|
||||||
VALUES ('{}', '{}', E'{}', current_timestamp)'''.format(
|
VALUES ('{}', E'{}', {})'''.format(
|
||||||
owner, submitter, text))
|
submitter, text, datetime.now().replace(tzinfo=timezone.utc)))
|
||||||
self.conn.commit()
|
|
||||||
cur.close()
|
|
||||||
self.num_quotes += 1
|
self.num_quotes += 1
|
||||||
|
logging.debug("Quote number %s inserted" % self.num_quotes)
|
||||||
|
|
||||||
def get(self, arg=None):
|
def get(self, arg=None):
|
||||||
if arg:
|
if arg:
|
||||||
|
@ -50,7 +43,10 @@ class Quote:
|
||||||
|
|
||||||
def _get_num_quotes(self):
|
def _get_num_quotes(self):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
cur.execute("SELECT COUNT(id) FROM quotes")
|
try:
|
||||||
|
cur.execute("SELECT COUNT(id) FROM quotes")
|
||||||
|
except sqlite3.OperationalError:
|
||||||
|
return 0
|
||||||
return cur.fetchone()[0]
|
return cur.fetchone()[0]
|
||||||
|
|
||||||
def _format(self, quotes):
|
def _format(self, quotes):
|
||||||
|
@ -63,3 +59,20 @@ class Quote:
|
||||||
return "No quote."
|
return "No quote."
|
||||||
return "{}. {}, {}".format(
|
return "{}. {}, {}".format(
|
||||||
data[0], data[1], datetime.strftime(data[2], '%Y'))
|
data[0], data[1], datetime.strftime(data[2], '%Y'))
|
||||||
|
|
||||||
|
def __connect(self):
|
||||||
|
self.conn = sqlite3.connect("data/quote.db")
|
||||||
|
|
||||||
|
def __init_table(self):
|
||||||
|
try:
|
||||||
|
self.__connect()
|
||||||
|
except sqlite3.OperationalError:
|
||||||
|
import os
|
||||||
|
os.makedirs("data")
|
||||||
|
self.__connect()
|
||||||
|
|
||||||
|
self.conn.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS quotes (
|
||||||
|
id INTEGER PRIMARY KEY, submitter TEXT,
|
||||||
|
text TEXT, timestamp TEXT
|
||||||
|
)''')
|
||||||
|
|
4
bot.py
4
bot.py
|
@ -1,10 +1,10 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from flask import Flask, Response
|
from flask import Flask, Response
|
||||||
from ameliabot import owncast
|
from ameliabot import owncast, __version__
|
||||||
from ameliabot.logger import logging
|
from ameliabot.logger import logging
|
||||||
|
|
||||||
|
|
||||||
logging.info("Loaded ameliabot v%s" % owncast.bot_version)
|
logging.info("Loaded %s v%s" % (owncast.config["bot_name"], __version__))
|
||||||
|
|
||||||
# the url of the Owncast API for bot posts
|
# the url of the Owncast API for bot posts
|
||||||
owncast_url = "{}/api/integrations/chat/send".format(
|
owncast_url = "{}/api/integrations/chat/send".format(
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
{
|
{
|
||||||
"owncast_server": "localhost",
|
"owncast_server": "localhost",
|
||||||
"access_token": "",
|
"access_token": "",
|
||||||
|
"bot_name": "ameliabot",
|
||||||
"streamer_name": "owncast streamer",
|
"streamer_name": "owncast streamer",
|
||||||
"quote_enabled": false,
|
"quote_enabled": false,
|
||||||
"quote_db_name": "",
|
|
||||||
"quote_db_user": "",
|
|
||||||
"quote_db_pass": ""
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue