Автор: Leo
Простой 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}/
Например, что бы запустить конкретный инстанс, нужно выполнить следующий скрипт:
#!/bin/bash
APP_INSTANCE=4o docker compose -p 4o -f ../docker-compose.yml up -d
Контроль доступа пользователей осуществляется при помощи списка разрешенных идентификаторов, перечисляемых в файле allowed.list
.
Бот помещен в Docker контейнер, конфигурация которого приведена ниже:
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:
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
Полный исходный код бота незамысловат, и приведен ниже:
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 контейнере. В качестве параметров указываются имя базы данных и пароль. Данный скрипт является частью системы для автоматической конфигурации сервера.
Основные этапы:
- Проверяем права суперпользователя и наличие обязательных аргументов.
- Определяем переменные, а так же проверяем наличие необходимых переменных окружения.
- Проверяем готовность контейнера PostgreSQL.
- Когда контейнер готов, скрипт выполняет команду внутри контейнера PostgreSQL для создания пользователя и базы данных, используя переданные аргументы.
Полный код скрипта приведен ниже:
#!/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
Кроме того, автор проекта ищет работу. Мое резюме.
Скрипт для добавления глобальных переменных сервера
Цель данного скрипта — легко, при помощи одной команды добавлять новые глобальные переменные в конфигурацию сервера. Данный скрипт является частью системы для автоматической конфигурации сервера.
Основные этапы:
- Проверяем, что в параметрах запуска скрипта указано имя и значение переменной. Если нет, завершаем скрипт.
- Проверяем, существует ли файл
/etc/environment
. Если нет, создаем его. - Проверяем, существует ли уже переменная в
/etc/environment
. Если да, обновляем ее, а если нет, создаем. - Экспортируем переменную окружения, делая ее доступной для всех дочерних процессов, которые запускаются из текущей сессии оболочки.
Полный код скрипта приведен ниже:
#!/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 файл. Кроме того, есть встроенная возможность генерировать в качестве параметр переменной пароли. Данный скрипт является частью системы для автоматической конфигурации сервера.
Основные этапы:
- Проверяем, указаны ли параметры запуска скрипта. Если первый параметр (имя переменной) не указан, завершаем скрипт. Если не указан второй параметр (значение переменной), генерируем в качестве значения новый пароль.
- Проверяем, существует ли .env файл. Если нет, создаем его.
- Проверяем, существует ли уже переменная в .env файле. Если да, то обновляем ее значение. Если нет, создаем новую переменную.
Полный код скрипта приведен ниже:
#!/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
. Данный скрипт является частью системы для автоматической конфигурации сервера.
Основные этапы:
- Определяем путь до cron.cfg
- Считываем текущие задания crontab
- Считываем содержимое файла cron.cfg. Пустые и #закомментированые строки пропускаем
- Проверяем, существует ли уже строка из cron.cfg в crontab
- Если строки нет в crontab, она добавляется туда
Полный код скрипта приведен ниже:
#!/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. Данный скрипт является частью системы для автоматической конфигурации сервера.
Основные этапы:
- Проверяем, что параметр порта задан и является числом
- Делаем резервную копию файла
/etc/ssh/sshd_config
- Редактируем параметр Port
- Перезапускаем SSH
Полный код скрипта приведен ниже:
#!/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. Данный скрипт является частью системы для автоматической конфигурации сервера.
Пример использования:
cd /data/utils
bash ssh-pw.sh n
# bash ssh-pw.sh y
Основные этапы:
- Делаем резервную копию файла
/etc/ssh/sshd_config
, в котором будут производиться правки. - Редактируем параметр
PasswordAuthentication yes
- Перезапускаем SSH
Полный код скрипта приведен ниже:
#!/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
Кроме того, автор проекта ищет работу. Мое резюме.
Скрипт для первоначальной инициализации сервера
Цель данного скрипта — полностью автоматизированное первоначальное конфигурирование нового сервера без стороннего вмешательства. Скрипт является частью системы для автоматической конфигурации сервера.
Основные этапы:
- Обновляем все пакеты
- Устанавливаем необходимые пакеты
- Настраиваем часовой пояс
- Создаем нового sudo пользователя
- Создаем главную директорию сервера /data
- Создаем директорию для бэкапов сервера /backups
- Изменяем порт SSH
- Отключаем авторизацию SSH по паролю
Полный код скрипта приведен ниже:
#!/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.
Если она у вас не установлена, это можно сделать командой:
apt install apache2-utils
Далее, что бы непосредственно сгенерировать файл, нужно выполнить команду:
htpasswd -b -c $HTPASSWD_FILE $USERNAME $PASSWORD
где:
$HTPASSWD_FILE
— имя генерируемого файла, напримерusersfile.htpasswd
$USERNAME
— имя пользователя, например admin$PASSWORD
— пароль, например 1234567890
В конечном итоге можно сделать скрипт, который который будет генерировать такой файл:
#!/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
, а затем запустите его, передавая имя пользователя и пароль в качестве аргументов:
./create_htpasswd.sh username password
Этот скрипт создаст новый файл usersfile.htpasswd
и сохранит в него пару username:password.
Если вам интересен наш проект, есть вопросы, замечания, или предложения — оставляйте комментарии или пишите на почту: checkerwars@mail.ru
Кроме того, автор проекта ищет работу. Мое резюме.