Простой ChatGPT бот для Telegram

В этом боте реализован лишь базовый функционал, позволяющий получать доступ к ChatGPT при помощи платного OpenAI API, выполняя запросы через мессенджер Telegram.

Бот написан на Node.js. Взаимодействие с Telegram API реализовано при помощи библиотеки telegraf, а доступ к OpenAI API осуществляется с использованием официальной библиотеки openai.

Кроме того, для беспрепятственного доступа к OpenAI API реализована возможность подключения через socks5 прокси. При этом необходимость использования прокси и его параметры указываются в общем конфиге.

Также стоит добавить, что бот поддерживает работу нескольких одновременно запущенных экземпляров. Это может быть полезно для реализации доступа для разных версий API ChatGPT, например один бот для ChatGPT 3.5, а второй для ChatGPT 4o. Для каждого хоста и экземпляра настраивается своя конфигурация, которая находится в:

/data/secrets/${SERVER_DOMAIN}/tgbot-chatgpt/${APP_INSTANCE}/

Например, что бы запустить конкретный инстанс, нужно выполнить следующий скрипт:

Bash
#!/bin/bash

APP_INSTANCE=4o docker compose -p 4o -f ../docker-compose.yml up -d


Контроль доступа пользователей осуществляется при помощи списка разрешенных идентификаторов, перечисляемых в файле allowed.list.

Бот помещен в Docker контейнер, конфигурация которого приведена ниже:

YAML
services:
  tgbot-chatgpt:
    container_name: tgbot-chatgpt-${APP_INSTANCE}
    build:
      context: .
    env_file:
      - /data/secrets/${SERVER_DOMAIN}/tgbot-chatgpt/${APP_INSTANCE}/app.env
    volumes:
      - .:/app:rw
      - app_node_modules:/app/node_modules
      - /data/secrets/${SERVER_DOMAIN}/tgbot-chatgpt/${APP_INSTANCE}/allowed.list:/app/allowed.list:ro
    command: npm run start
    restart: unless-stopped
volumes:
  node_modules:
  app_node_modules:

Dockerfile
FROM node:22.7.0-alpine3.19

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY /src /app/src

ENV NODE_PATH=./node_modules

ENV HELLO_FILE=hello.txt
ENV ALLOWED_FILE=allowed.list


Полный исходный код бота незамысловат, и приведен ниже:

JavaScript
import { Telegraf } from 'telegraf';
import { message } from 'telegraf/filters';
import { code } from 'telegraf/format';
import OpenAI from 'openai';
import { promises as fs } from 'fs';
import { SocksProxyAgent } from 'socks-proxy-agent';

const isInstruct = process.env.API_INSTRUCT === 'true';

const proxyEnable = process.env.PROXY_ENABLE;
let proxyConfig = {};

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const timeout = parseInt(process.env.TIMEOUT, 10);
let allowedUsers = [];

console.log("GPT_MODEL: " + process.env.GPT_MODEL);
console.log("API_INSTRUCT: " + process.env.API_INSTRUCT);

if(proxyEnable === 'true') {
    const proxyHost = process.env.PROXY_HOST;
    const proxyPort = process.env.PROXY_PORT;
    const proxyLogin = process.env.PROXY_LOGIN;
    const proxyPassword = process.env.PROXY_PASSWORD;
    const proxyOptions = `socks5://${proxyLogin}:${proxyPassword}@${proxyHost}:${proxyPort}`;
    const proxyAgent = new SocksProxyAgent(proxyOptions);
    proxyConfig = { httpAgent: proxyAgent, httpsAgent: proxyAgent };
}

const bot = new Telegraf(
        process.env.BOT_TOKEN,
        { handlerTimeout: timeout }
    );

bot.command('new', initCommand);
bot.command('help', initCommand);
bot.command('start', initCommand);

async function chatGPT(content) {
    try {
        const chatCompletion = await openai.chat.completions.create({
            messages: [
                { role: 'system', content: 'Ответ пиши без markdown.' },
                { role: 'user', content }
            ],
            model: process.env.GPT_MODEL,
        }, proxyConfig);
        return chatCompletion.choices[0].message.content;
    } catch (e) {
        console.log('Error while gpt chat', e.message);
    }
}

async function chatGPTinstruct(content) {
    try {
        const chatCompletion = await openai.completions.create({
            model: process.env.GPT_MODEL,
            prompt: content,
            max_tokens: 3000
        }, proxyConfig);
        return chatCompletion.choices[0].text;
    } catch (e) {
        console.log('Error while gpt chat', e.message);
    }
}

bot.on(message('text'), async (ctx) => {
    if (await auth(ctx.message.from.id)) {
        try {
            await ctx.reply(code('🕰️⏰🕙⏱️⏳...'));

            let responce;
            if(isInstruct) {
                responce = await chatGPTinstruct(ctx.message.text);
            } else {
                responce = await chatGPT(ctx.message.text);
            }

            if(responce) {
                await ctx.reply(responce);
            } else {
                await ctx.reply("Не могу ответить");
            }
        } catch (e) {
            console.log('Error GPT', e.message);
        }
    } else {
        await ctx.reply(code(`Access denied for ${ctx.message.from.first_name} (${ctx.message.from.id})`));
    }
});

async function fileToString(filePath) {
    try {
        return await fs.readFile(filePath, { encoding: 'utf-8' });
    } catch {
        return 'Error';
    }
}

async function initCommand(ctx) {
    const helloText = await fileToString(process.env.HELLO_FILE);
    await ctx.reply(helloText);
}

async function auth(fromId) {
    try {
        return allowedUsers.includes(fromId);
    } catch(error) {
        console.error(error);
        return false;
    }
}

async function loadAllowedIds(filePath) {
    try {
        const data = await fileToString(filePath);
        const lines = data.split('\n');
        return lines
            .map(line => line.split('//')[0].trim())
            .filter(line => line.length > 0)
            .map(line => parseInt(line, 10))
            .filter(id => !isNaN(id));
    } catch (err) {
        console.error(err);
        return [];
    }
}

async function start() {
    allowedUsers = await loadAllowedIds(process.env.ALLOWED_FILE);
    await bot.launch();
}

start();


Дополнительно в этом коде присутствует реализация доступа к устаревшей версии API instruct. Необходимость его использования задается соответствующим флагом в конфигурационном файле.

Из основных недостатков этой реализации можно отметить отсутствие диалогового режима, и лишь базовая система контроля доступа пользователей. Так же отсутствует контроль потраченных токенов. Кроме того, в некоторых случаях, могла бы быть полезной работа с изображениями, которая так же отсутствует. Но для личного использования этот бот отлично себя зарекомендовал.

Скрипт для автоматизации создания базы данных в Docker контейнере с PostgreSQL

Этот Bash скрипт создает новую пустую базу данных в СУБД PostgreSQL, которая находится в Docker контейнере. В качестве параметров указываются имя базы данных и пароль. Данный скрипт является частью системы для автоматической конфигурации сервера.

Основные этапы:

  1. Проверяем права суперпользователя и наличие обязательных аргументов.
  2. Определяем переменные, а так же проверяем наличие необходимых переменных окружения.
  3. Проверяем готовность контейнера PostgreSQL.
  4. Когда контейнер готов, скрипт выполняет команду внутри контейнера PostgreSQL для создания пользователя и базы данных, используя переданные аргументы.

Полный код скрипта приведен ниже:

Bash
#!/bin/bash

if [ "$(id -u)" != "0" ]; then
    echo -e "\033[31mThis script requires superuser rights\033[0m"
    exit 0
fi

if [ -z "$1" ] || [ -z "$2" ]; then
  echo "Please provide all arguments: CREATE_DB_NAME, CREATE_DB_PASSWORD"
  exit 1
fi

trap 'echo -e "\033[31minit-db.sh: An error has occurred\033[0m"; exit 1' ERR
set -e

export DEBIAN_FRONTEND=noninteractive

PG_CONTAINER=postgres
CREATE_DB_NAME=$1
CREATE_DB_PASSWORD=$2

: "${POSTGRES_USER:?}"
: "${POSTGRES_DB:?}"

# Function to check container status:
check_container_state() {
  state=$(sudo docker inspect -f '{{.State.Health.Status}}' "$PG_CONTAINER" 2>/dev/null)
  
  if [ "$state" = "healthy" ]; then
    return 0
  fi
  
  state=$(sudo docker inspect -f '{{.State.Status}}' "$PG_CONTAINER" 2>/dev/null)
  
  if [ "$state" = "running" ]; then
    return 0
  fi
  
  return 1
}

echo "Waiting for PostgreSQL container to start..."
while ! check_container_state; do
  echo "Waiting for PostgreSQL container to be ready..."
  sleep 5
done

echo "PostgreSQL container is ready. Init DB..."

sudo docker exec -i $PG_CONTAINER psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" <<-EOSQL
  CREATE USER "$CREATE_DB_NAME" WITH ENCRYPTED PASSWORD '$CREATE_DB_PASSWORD';
  CREATE DATABASE "$CREATE_DB_NAME";
  GRANT ALL PRIVILEGES ON DATABASE "$CREATE_DB_NAME" TO "$CREATE_DB_NAME";
  ALTER DATABASE "$CREATE_DB_NAME" OWNER TO "$CREATE_DB_NAME";
EOSQL


trap - ERR
echo -e "\033[32mDatabase $CREATE_DB_NAME created successfully\033[0m"


Данный скрипт является частью нашего репозитория utils.



Если вам интересен наш проект, есть вопросы, замечания, или предложения — оставляйте комментарии или пишите на почту: checkerwars@mail.ru

Кроме того, автор проекта ищет работу. Мое резюме.

Скрипт для добавления глобальных переменных сервера

Цель данного скрипта — легко, при помощи одной команды добавлять новые глобальные переменные в конфигурацию сервера. Данный скрипт является частью системы для автоматической конфигурации сервера.

Основные этапы:

  1. Проверяем, что в параметрах запуска скрипта указано имя и значение переменной. Если нет, завершаем скрипт.
  2. Проверяем, существует ли файл /etc/environment. Если нет, создаем его.
  3. Проверяем, существует ли уже переменная в /etc/environment. Если да, обновляем ее, а если нет, создаем.
  4. Экспортируем переменную окружения, делая ее доступной для всех дочерних процессов, которые запускаются из текущей сессии оболочки.

Полный код скрипта приведен ниже:

Bash
#!/bin/bash

if [ "$(id -u)" != "0" ]; then
    echo -e "\033[31mThis script requires superuser rights.\033[0m"
    exit 0
fi

if [ -z "$1" ] || [ -z "$2" ]; then
  echo "Please provide both the VAR_NAME and VAR_VALUE as arguments."
  exit 1
fi

trap 'echo -e "\033[31mSomething went wrong\033[0m"; exit 1' ERR
set -e

export DEBIAN_FRONTEND=noninteractive

ENV_VAR_NAME=$1
ENV_VAR_VALUE=$2

# Create a file if it does not exist
if [ ! -f /etc/environment ]; then
    touch /etc/environment
fi

# Checking if a variable already exists
if grep -q "^${ENV_VAR_NAME}=" /etc/environment; then
    # If the variable exists, update its value
    sed -i "s/^${ENV_VAR_NAME}=.*/${ENV_VAR_NAME}=${ENV_VAR_VALUE}/" /etc/environment
else
    # If the variable does not exist, add it
    echo "${ENV_VAR_NAME}=${ENV_VAR_VALUE}" | tee -a /etc/environment
fi

export "${ENV_VAR_NAME}=${ENV_VAR_VALUE}"

trap - ERR
echo "Environment variable ${ENV_VAR_NAME} set to:"
printenv "${ENV_VAR_NAME}"


Данный скрипт является частью нашего репозитория utils.



Если вам интересен наш проект, есть вопросы, замечания, или предложения — оставляйте комментарии или пишите на почту: checkerwars@mail.ru

Кроме того, автор проекта ищет работу. Мое резюме.

Скрипт для добавления переменных в конфигурацию .env

Цель данного скрипта — легко, при помощи одной команды добавлять новые переменные в .env файл. Кроме того, есть встроенная возможность генерировать в качестве параметр переменной пароли. Данный скрипт является частью системы для автоматической конфигурации сервера.

Основные этапы:

  1. Проверяем, указаны ли параметры запуска скрипта. Если первый параметр (имя переменной) не указан, завершаем скрипт. Если не указан второй параметр (значение переменной), генерируем в качестве значения новый пароль.
  2. Проверяем, существует ли .env файл. Если нет, создаем его.
  3. Проверяем, существует ли уже переменная в .env файле. Если да, то обновляем ее значение. Если нет, создаем новую переменную.

Полный код скрипта приведен ниже:

Bash
#!/bin/bash

if [ -z "$1" ]; then
  echo "Use: $0 VARIABLE_NAME (VALUE)"
  exit 1
fi

VARIABLE_NAME=$1
ENV_FILE="/data/secrets/$SERVER_DOMAIN/$SERVER_DOMAIN.env"

# Password gen: 20chars,0-9,a-z
generate_random_password() {
  #tr -dc 'a-z0-9' </dev/urandom | head -c 20
  pwgen -s 20 1
}

trap 'echo -e "\033[31menv-gen.sh: Something went wrong\033[0m"; exit 1' ERR
set -e

export DEBIAN_FRONTEND=noninteractive

echo "If the second parameter is specified, use it as the value of the variable..."
if [ -n "$2" ]; then
  VALUE=$2
else
  VALUE=$(generate_random_password)
fi

echo "Creating .env file if it doesn't exist..."
if [ ! -f "$ENV_FILE" ]; then
  echo "# Creating directories if they don't exist..."
  mkdir -p "$(dirname "$ENV_FILE")"
  touch "$ENV_FILE"
fi

echo "Update or add a variable to the .env file"
if grep -q "^$VARIABLE_NAME=" "$ENV_FILE"; then
  echo "The variable exists, update its value..."
  sed -i "s/^$VARIABLE_NAME=.*/$VARIABLE_NAME=$VALUE/" "$ENV_FILE"
else
  echo "The variable does not exist, add it to the file..."
  echo "$VARIABLE_NAME=$VALUE" >> "$ENV_FILE"
fi

trap - ERR
echo "Variable $VARIABLE_NAME successfully updated/added to $ENV_FILE"


Данный скрипт является частью нашего репозитория utils.



Если вам интересен наш проект, есть вопросы, замечания, или предложения — оставляйте комментарии или пишите на почту: checkerwars@mail.ru

Кроме того, автор проекта ищет работу. Мое резюме.

Скрипт добавления Cron-заданий из файла

Цель данного скрипта — легко, при помощи одной команды переносить задания для Cron, описанные в файле cron.cfg непосредственно в конфигурацию crontab. Данный скрипт является частью системы для автоматической конфигурации сервера.

Основные этапы:

  1. Определяем путь до cron.cfg
  2. Считываем текущие задания crontab
  3. Считываем содержимое файла cron.cfg. Пустые и #закомментированые строки пропускаем
  4. Проверяем, существует ли уже строка из cron.cfg в crontab
  5. Если строки нет в crontab, она добавляется туда

Полный код скрипта приведен ниже:

Bash
#!/bin/bash

CRON_LIST=/data/$SRV_START_DIR/config/cron.cfg

trap 'echo -e "\033[31mcrom-list.sh: Something went wrong\033[0m"; exit 1' ERR
set -e

export DEBIAN_FRONTEND=noninteractive

echo "Checking for the presence of the cron.cfg file..."
if [ ! -f $CRON_LIST ]; then
  echo "cron.cfg file not found!"
  exit 1
fi

echo "Reading current crontab jobs into a variable..."
current_crontab=$(crontab -l 2>/dev/null || true)

echo "Iterate through the lines of the cron.list file..."
while IFS= read -r line; do
  # Skip blank lines and comments
  if [[ -z "$line" || "$line" == \#* ]]; then
    continue
  fi

  echo "Checking if a job exists in the current crontab..."
  if echo "$current_crontab" | grep -Fq "$line"; then
    echo "The task already exists: $line"
  else
    echo "Adding a job to crontab..."
    (crontab -l 2>/dev/null || true; echo "$line") | crontab -
    echo "Task added: $line"
  fi
done < "$CRON_LIST"

trap - ERR
echo "Cron jobs from $CRON_LIST added successfully"


Данный скрипт является частью нашего репозитория utils.



Если вам интересен наш проект, есть вопросы, замечания, или предложения — оставляйте комментарии или пишите на почту: checkerwars@mail.ru

Кроме того, автор проекта ищет работу. Мое резюме.

Скрипт для изменения SSH порта

Цель данного скрипта — легко, при помощи одной команды изменять порт SSH. Данный скрипт является частью системы для автоматической конфигурации сервера.

Основные этапы:

  1. Проверяем, что параметр порта задан и является числом
  2. Делаем резервную копию файла /etc/ssh/sshd_config
  3. Редактируем параметр Port
  4. Перезапускаем SSH

Полный код скрипта приведен ниже:

Bash
#!/bin/bash

if [ "$(id -u)" != "0" ]; then
    echo -e "\033[31mThis script requires superuser rights.\033[0m"
    exit 0
fi

if [ -z "$1" ]; then
  echo "Please provide the port as an argument"
  exit 1
fi

NEW_PORT="$1"
SSH_CONFIG_FILE="/etc/ssh/sshd_config"

# Проверяем, что параметр является числом
if ! [[ "$NEW_PORT" =~ ^[0-9]+$ ]]; then
  echo -e "\033[31mPort must be a number\033[0m"
  exit 1
fi

trap 'echo -e "\033[31mSomething went wrong\033[0m"; exit 1' EXIT
set -e

echo "Change SSH port to $NEW_PORT..."
cp $SSH_CONFIG_FILE $SSH_CONFIG_FILE.bak
sed -i "s/^#\?Port [0-9]*/Port $NEW_PORT/" $SSH_CONFIG_FILE

systemctl daemon-reload
systemctl restart ssh

trap - EXIT
echo "SSH port successfully changed to $NEW_PORT."


Данный скрипт является частью нашего репозитория utils.



Если вам интересен наш проект, есть вопросы, замечания, или предложения — оставляйте комментарии или пишите на почту: checkerwars@mail.ru

Кроме того, автор проекта ищет работу. Мое резюме.

Скрипт для отключения/включения входа по паролю через SSH

Цель данного скрипта — легко, при помощи одной команды отключать/включать вход по паролю через SSH. Данный скрипт является частью системы для автоматической конфигурации сервера.

Пример использования:

Bash
cd /data/utils
bash ssh-pw.sh n
# bash ssh-pw.sh y

Основные этапы:

  1. Делаем резервную копию файла /etc/ssh/sshd_config, в котором будут производиться правки.
  2. Редактируем параметр PasswordAuthentication yes
  3. Перезапускаем SSH

Полный код скрипта приведен ниже:

Bash
#!/bin/bash

if [ "$(id -u)" != "0" ]; then
    echo -e "\033[31mThis script requires superuser rights\033[0m"
    exit 0
fi

if [ "$#" -ne 1 ]; then
    echo "Usage: $0 <y/n>"
    exit 1
fi

if [ "$1" != "y" ] && [ "$1" != "n" ]; then
    echo "Invalid argument. Use 'y' to enable password authentication and 'n' to disable it"
    exit 1
fi

ENABLE_PASSWORD=$1
SSHD_CONFIG="/etc/ssh/sshd_config"

trap 'echo -e "\033[31mSomething went wrong\033[0m"; exit 1' ERR
set -e

echo "Backup $SSHD_CONFIG..."
cp $SSHD_CONFIG $SSHD_CONFIG.backup

if [ "$ENABLE_PASSWORD" == "y" ]; then
    sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication yes/' $SSHD_CONFIG
    sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' $SSHD_CONFIG
else
    sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/' $SSHD_CONFIG
    sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' $SSHD_CONFIG
fi

echo "Restart SSH for apply password $1"
systemctl daemon-reload
systemctl restart ssh

trap - ERR

if [ "$1" == "y" ]; then
    echo "Password authentication has been enabled"
else
    echo "Password authentication has been disabled"
fi


Данный скрипт является частью нашего репозитория utils.



Если вам интересен наш проект, есть вопросы, замечания, или предложения — оставляйте комментарии или пишите на почту: checkerwars@mail.ru

Кроме того, автор проекта ищет работу. Мое резюме.

Скрипт для первоначальной инициализации сервера

Цель данного скрипта — полностью автоматизированное первоначальное конфигурирование нового сервера без стороннего вмешательства. Скрипт является частью системы для автоматической конфигурации сервера.

Основные этапы:

  1. Обновляем все пакеты
  2. Устанавливаем необходимые пакеты
  3. Настраиваем часовой пояс
  4. Создаем нового sudo пользователя
  5. Создаем главную директорию сервера /data
  6. Создаем директорию для бэкапов сервера /backups
  7. Изменяем порт SSH
  8. Отключаем авторизацию SSH по паролю

Полный код скрипта приведен ниже:

Bash
#!/bin/bash

USERNAME=usradmin
SSH_PORT=2525

if [ "$(id -u)" != "0" ]; then
    echo -e "\033[31mThis script requires superuser rights.\033[0m"
    exit 0
fi

trap 'echo -e "\033[31mSomething went wrong\033[0m"; exit 1' ERR
set -e

export DEBIAN_FRONTEND=noninteractive

echo "Upgrade packages..."
apt-get update
apt-get upgrade -y

echo "Install apps..."
apt-get install -y mc vim pwgen cron

echo "Set timezone..."
timedatectl set-timezone Asia/Yekaterinburg
echo "Timezone changed:"
timedatectl

echo "Add new sudo user..."
if id "$USERNAME" &>/dev/null; then
    echo "User $USERNAME already exists"
else
    useradd -m -s /bin/bash "$USERNAME"
    usermod -aG sudo "$USERNAME"
fi

echo "Configure /data privilegies..."
chown "$USERNAME":"$USERNAME" /data
chmod 770 /data

echo "Create /backups directory..."
mkdir -p /backups
chown "$USERNAME":"$USERNAME" /backups
chmod 770 /backups

echo "Change SSH port to $SSH_PORT..."
cd /data/utils
bash ssh-port.sh $SSH_PORT

echo "Disable SSH password auth..."
bash ssh-pw.sh n

trap - ERR
echo "Init complete"


Данный скрипт является частью нашего репозитория utils.



Если вам интересен наш проект, есть вопросы, замечания, или предложения — оставляйте комментарии или пишите на почту: checkerwars@mail.ru

Кроме того, автор проекта ищет работу. Мое резюме.

Генерация .htpasswd файла для HTTP-аутентификации

Для реализации HTTP-аутентификации у себя на сервере вам потребуется создать файл, в котором будут храниться учетные данные пользователя.

Например, для пользователя admin с паролем 1234567890 содержимое файла будет выглядеть следующим образом:

admin:$apr1$x.EtyPS5$nSE6Zcy8mewXqv2f4ilFU1

Чтобы сгенерировать файл с таким содержимым можно воспользоваться утилитой htpasswd, которая обычно поставляется с пакетом Apache.

Если она у вас не установлена, это можно сделать командой:

Bash
apt install apache2-utils


Далее, что бы непосредственно сгенерировать файл, нужно выполнить команду:

Bash
htpasswd -b -c $HTPASSWD_FILE $USERNAME $PASSWORD

где:

  • $HTPASSWD_FILE — имя генерируемого файла, например usersfile.htpasswd
  • $USERNAME — имя пользователя, например admin
  • $PASSWORD — пароль, например 1234567890

В конечном итоге можно сделать скрипт, который который будет генерировать такой файл:

Bash
#!/bin/bash

# Проверяем количество аргументов
if [ $# -ne 2 ]; then
  echo "Использование: $0 имя_пользователя пароль"
  exit 1
fi

# Сохраняем аргументы в переменные
USERNAME=$1
PASSWORD=$2

# Файл для хранения паролей
HTPASSWD_FILE="usersfile.htpasswd"

# Проверяем наличие утилиты htpasswd
if ! command -v htpasswd &> /dev/null
then
    echo "Утилита htpasswd не найдена. Установите пакет apache2-utils или аналогичный."
    exit
fi

# Создаем или обновляем .htpasswd файл
# -b для использования в режиме batch, -c для создания нового файла
htpasswd -b -c $HTPASSWD_FILE $USERNAME $PASSWORD

echo "Пользователь $USERNAME добавлен в $HTPASSWD_FILE"


Сохраните этот скрипт в файл, например, create_htpasswd.sh, сделайте его исполняемым с помощью команды chmod +x create_htpasswd.sh, а затем запустите его, передавая имя пользователя и пароль в качестве аргументов:

Bash
./create_htpasswd.sh username password


Этот скрипт создаст новый файл usersfile.htpasswd и сохранит в него пару username:password.


Если вам интересен наш проект, есть вопросы, замечания, или предложения — оставляйте комментарии или пишите на почту: checkerwars@mail.ru

Кроме того, автор проекта ищет работу. Мое резюме.