RRichard Chienfix docker image
a406f446创建于 2020年3月31日历史提交
import os
import json
import shutil
import logging
from configparser import ConfigParser

logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')

APP_ID = 'io.github.richardchien.coolqhttpapi'

COOLQ_DIR = '/home/user/coolq'
COOLQ_CONFIG_FILE = os.path.join(COOLQ_DIR, 'conf', 'CQP.cfg')
COOLQ_APP_DIR = os.path.join(COOLQ_DIR, 'app')
COOLQ_APP_DATA_DIR = os.path.join(COOLQ_DIR, 'data', 'app')

APP_DIR_OLD = os.path.join(COOLQ_APP_DIR, APP_ID)
APP_DIR = os.path.join(COOLQ_APP_DATA_DIR, APP_ID)
APP_CONFIG_DIR = os.path.join(APP_DIR, 'config')
VERSION_LOCK_FILE = os.path.join(APP_DIR, 'version.lock')

CPK_NAME = f'{APP_ID}.cpk'
CPK_FILE = f'/home/user/{CPK_NAME}'


def strtobool(s):
    return s.lower() in {'1', 'true', 'yes', 'on'}


def touch(path):
    with open(path, 'a'):
        os.utime(path, None)


def makedirs(dir, mode=0o755, exist_ok=True):
    os.makedirs(dir, mode=mode, exist_ok=exist_ok)


def copy_cpk():
    makedirs(COOLQ_APP_DIR)
    shutil.copyfile(CPK_FILE, os.path.join(COOLQ_APP_DIR, CPK_NAME))


def enable_plugin():
    coolq_config = ConfigParser()
    coolq_config.read(COOLQ_CONFIG_FILE)
    if 'App' in coolq_config:
        coolq_config['App'][APP_ID + '.status'] = '1'
    else:
        coolq_config['App'] = {
            APP_ID + '.status': '1'
        }
    with open(COOLQ_CONFIG_FILE, 'w') as config_file:
        coolq_config.write(config_file)


def is_first_start():
    return not os.path.exists(APP_DIR)


def version_locked():
    return os.path.exists(VERSION_LOCK_FILE)


def force_env():
    try:
        return strtobool(os.getenv('FORCE_ENV', 'false'))
    except ValueError:
        return False


def app_config_paths(config_name, include_general=False):
    paths = [
        os.path.join(APP_CONFIG_DIR, config_name + '.ini'),
        os.path.join(APP_CONFIG_DIR, config_name + '.cfg'),
        os.path.join(APP_CONFIG_DIR, config_name + '.json'),
    ]
    if include_general:
        paths.extend([
            os.path.join(APP_CONFIG_DIR, 'general.ini'),
            os.path.join(APP_CONFIG_DIR, 'general.cfg'),
            os.path.join(APP_CONFIG_DIR, 'general.json'),
        ])
    return paths


def app_config_exists(config_name, include_general=False):
    for p in app_config_paths(config_name, include_general):
        if os.path.exists(p):
            return True
    return False


def remove_app_config(config_name, include_general=False):
    for p in app_config_paths(config_name, include_general):
        if os.path.exists(p):
            os.remove(p)


def app_config_format():
    if os.path.exists(os.path.join(APP_CONFIG_DIR, 'general.json')) and \
            not os.path.exists(
                os.path.join(APP_CONFIG_DIR, 'general.ini')) and \
            not os.path.exists(
                os.path.join(APP_CONFIG_DIR, 'general.cfg')):
        return 'json'
    return 'ini'


def write_app_config(config_name, app_config):
    makedirs(APP_CONFIG_DIR)
    file_format = app_config_format()
    with open(os.path.join(APP_CONFIG_DIR, config_name + '.' + file_format),
              'w') as config_file:
        if file_format == 'json':
            json.dump(dict(app_config[config_name].items()),
                      config_file, ensure_ascii=False, indent=4)
        else:
            app_config.write(config_file)


def move_old_app_dir():
    if os.path.exists(APP_DIR_OLD) and not os.path.exists(APP_DIR):
        logging.debug(f'检测到旧的插件数据,正在复制到新位置 {APP_DIR}……')
        makedirs(os.path.dirname(APP_DIR))
        shutil.copytree(APP_DIR_OLD, APP_DIR)
        app_data_backup_dir = f'{APP_DIR_OLD.rstrip(os.path.sep)}.bak'
        shutil.move(APP_DIR_OLD, app_data_backup_dir)
        logging.debug(f'复制插件数据成功,旧的插件数据已备份在 {app_data_backup_dir}。')


def bootstrap():
    move_old_app_dir()

    if is_first_start():
        logging.debug('容器首次启动,开始初始化……')
        makedirs(APP_DIR)
        logging.debug('正在安装插件……')
        copy_cpk()
        touch(VERSION_LOCK_FILE)
        logging.debug('正在启用插件……')
        enable_plugin()
    elif version_locked():
        logging.debug('插件版本已锁定,正在覆盖 cpk 文件……')
        copy_cpk()

    config_name = os.getenv('COOLQ_ACCOUNT', 'general')
    app_config = ConfigParser()
    app_config[config_name] = {}
    for key, value in os.environ.items():
        if not key.startswith('CQHTTP_'):
            continue

        key = key[len('CQHTTP_'):]
        app_config[config_name][key.lower()] = value

    if not app_config_exists(config_name, include_general=False):
        logging.debug('配置文件不存在,正在根据配置文件生成初始配置文件……')
        write_app_config(config_name, app_config)
    elif force_env():
        logging.debug('已强制使用环境变量,正在覆盖配置文件……')
        remove_app_config(config_name, include_general=False)
        write_app_config(config_name, app_config)

    logging.info('CQHTTP 插件 bootstrap 成功。')


if __name__ == '__main__':
    try:
        bootstrap()
    except Exception as e:
        logging.exception(e)
        logging.error('CQHTTP 插件 bootstrap 失败。')