Add: Docker
This commit is contained in:
parent
4c8288c0d2
commit
ce01bcc3d4
8
.air.bot.toml
Normal file
8
.air.bot.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
root = "."
|
||||||
|
tmp_dir = "tmp_bot"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
cmd = "go build -o ./tmp_bot/main ./cmd/bot/main.go"
|
||||||
|
bin = "./tmp_bot/main"
|
||||||
|
delay = 1000
|
||||||
|
|
8
.air.web.toml
Normal file
8
.air.web.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
root = "."
|
||||||
|
tmp_dir = "tmp_web"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
cmd = "go build -o ./tmp_web/main ./cmd/web/main.go"
|
||||||
|
bin = "./tmp_web/main"
|
||||||
|
delay = 1000
|
||||||
|
|
26
Dockerfile.bot
Normal file
26
Dockerfile.bot
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
FROM golang:1.21.4
|
||||||
|
|
||||||
|
ARG DOCKER_ENV=production # Set default build-time value, not persisted in the container
|
||||||
|
ENV DOCKER_ENV=${DOCKER_ENV} # Set runtime environment variable inside the container
|
||||||
|
|
||||||
|
WORKDIR /code
|
||||||
|
|
||||||
|
RUN echo "Building with DOCKER_ENV=${DOCKER_ENV}"
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN go mod tidy && \
|
||||||
|
if [ "$DOCKER_ENV" = "local" ]; then \
|
||||||
|
echo "Local environment detected, installing Air..."; \
|
||||||
|
go install github.com/cosmtrek/air@v1.49.0; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
CMD if [ "$DOCKER_ENV" = "local" ]; then \
|
||||||
|
echo "Local environment detected, running Air..." && \
|
||||||
|
air -c .air.bot.toml; \
|
||||||
|
else \
|
||||||
|
echo "Production environment detected, building and running bot..."; \
|
||||||
|
go build -o bot ./cmd/bot/main.go && ./bot; \
|
||||||
|
fi
|
26
Dockerfile.web
Normal file
26
Dockerfile.web
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
FROM golang:1.21.4
|
||||||
|
|
||||||
|
ARG DOCKER_ENV=production # Set default build-time value, not persisted in the container
|
||||||
|
ENV DOCKER_ENV=${DOCKER_ENV} # Set runtime environment variable inside the container
|
||||||
|
|
||||||
|
WORKDIR /code
|
||||||
|
|
||||||
|
RUN echo "Building with DOCKER_ENV=${DOCKER_ENV}"
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN go mod tidy && \
|
||||||
|
if [ "$DOCKER_ENV" = "local" ]; then \
|
||||||
|
echo "Local environment detected, installing Air..."; \
|
||||||
|
go install github.com/cosmtrek/air@v1.49.0; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
CMD if [ "$DOCKER_ENV" = "local" ]; then \
|
||||||
|
echo "Local environment detected, running Air..." && \
|
||||||
|
air -c .air.web.toml; \
|
||||||
|
else \
|
||||||
|
echo "Production environment detected, building and running web..."; \
|
||||||
|
go build -o web ./cmd/web/main.go && ./web; \
|
||||||
|
fi
|
220
Makefile
Normal file
220
Makefile
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
##
|
||||||
|
# Makefile to help manage docker-compose services
|
||||||
|
#
|
||||||
|
|
||||||
|
# Include environment files
|
||||||
|
include .env
|
||||||
|
export
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
THIS_FILE := $(lastword $(MAKEFILE_LIST))
|
||||||
|
DOCKER := $(shell which docker)
|
||||||
|
DOCKER_COMPOSE := $(shell which docker-compose)
|
||||||
|
|
||||||
|
IMAGE_DEFAULT := web
|
||||||
|
CONTAINER_DEFAULT := web
|
||||||
|
|
||||||
|
SHELL_CMD := /bin/bash
|
||||||
|
|
||||||
|
# Docker compose files
|
||||||
|
DOCKER_COMPOSE_FILES := -f docker-compose.yml
|
||||||
|
ifeq ($(DOCKER_ENV),local)
|
||||||
|
DOCKER_COMPOSE_FILES := -f docker-compose.yml
|
||||||
|
endif
|
||||||
|
ifeq ($(DOCKER_ENV),production)
|
||||||
|
DOCKER_COMPOSE_FILES := -f docker-compose.yml
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Services
|
||||||
|
SERVICES_DEFAULT := bot web
|
||||||
|
ifeq ($(DOCKER_ENV),local)
|
||||||
|
SERVICES_DEFAULT := bot web
|
||||||
|
endif
|
||||||
|
ifeq ($(DOCKER_ENV),production)
|
||||||
|
SERVICES_DEFAULT := bot web
|
||||||
|
endif
|
||||||
|
SERVICE_DEFAULT := bot
|
||||||
|
|
||||||
|
container ?= $(CONTAINER_DEFAULT)
|
||||||
|
image ?= $(IMAGE_DEFAULT)
|
||||||
|
service ?=
|
||||||
|
services ?= $(SERVICES_DEFAULT)
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# help
|
||||||
|
#
|
||||||
|
help:
|
||||||
|
ifeq ($(CONTAINER_DEFAULT),)
|
||||||
|
$(warning WARNING: CONTAINER_DEFAULT is not set. Please edit makefile)
|
||||||
|
endif
|
||||||
|
@echo
|
||||||
|
@echo "Make targets:"
|
||||||
|
@echo
|
||||||
|
@cat $(THIS_FILE) | \
|
||||||
|
sed -n -E 's/^([^.][^: ]+)\s*:(([^=#]*##\s*(.*[^[:space:]])\s*)|[^=].*)$$/ \1 \4/p' | \
|
||||||
|
sort -u | \
|
||||||
|
expand -t15
|
||||||
|
@echo
|
||||||
|
@echo
|
||||||
|
@echo "Target arguments:"
|
||||||
|
@echo
|
||||||
|
@echo " " "service" "\t" "Target service for docker-compose actions (default=all-services)"
|
||||||
|
@echo " " " " "\t" " - make start"
|
||||||
|
@echo " " " " "\t" " - make start service=app"
|
||||||
|
@echo " " "services" "\t" "Target services for docker-compose actions (default=all-services, space separated)"
|
||||||
|
@echo " " " " "\t" " - make stop services='app db'"
|
||||||
|
@echo " " "container""\t" "Target container for docker actions (default='$(CONTAINER_DEFAULT)')"
|
||||||
|
@echo " " " " "\t" " - make bash container=$(container)"
|
||||||
|
@echo " " "image" "\t" "Target image for interactive shell (default='$(IMAGE_DEFAULT)')"
|
||||||
|
@echo " " " " "\t" " - make it image=$(image)"
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# services
|
||||||
|
#
|
||||||
|
services: ## Lists services
|
||||||
|
@$(DOCKER_COMPOSE) $(DOCKER_COMPOSE_FILES) ps --services
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# start
|
||||||
|
#
|
||||||
|
all: dev ## See 'dev'
|
||||||
|
start: dev ## See 'dev'
|
||||||
|
dev: ## Start containers for development [service|services]
|
||||||
|
@$(DOCKER_COMPOSE) $(DOCKER_COMPOSE_FILES) up -d $(services)
|
||||||
|
$(MAKE) logs
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# stop
|
||||||
|
#
|
||||||
|
stop: ## Stop containers [service|services]
|
||||||
|
@$(DOCKER_COMPOSE) $(DOCKER_COMPOSE_FILES) stop $(services)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# restart
|
||||||
|
#
|
||||||
|
restart: ## Restart containers [service|services]
|
||||||
|
@$(DOCKER_COMPOSE) $(DOCKER_COMPOSE_FILES) restart $(services)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# down
|
||||||
|
#
|
||||||
|
down: ## Removes containers (preserves images and volumes)
|
||||||
|
@$(DOCKER_COMPOSE) $(DOCKER_COMPOSE_FILES) down
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# build
|
||||||
|
#
|
||||||
|
build: ## Builds service images [service|services]
|
||||||
|
@$(DOCKER_COMPOSE) $(DOCKER_COMPOSE_FILES) build --build-arg DOCKER_ENV=${DOCKER_ENV} $(services)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# rebuild
|
||||||
|
#
|
||||||
|
rebuild: ## Build containers without cache [service|services]
|
||||||
|
@$(DOCKER_COMPOSE) $(DOCKER_COMPOSE_FILES) build --build-arg DOCKER_ENV=${DOCKER_ENV} --no-cache $(services)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# ps
|
||||||
|
#
|
||||||
|
status: ps ## See 'ps'
|
||||||
|
ps: ## Show status of containers [service|services]
|
||||||
|
@$(DOCKER_COMPOSE) $(DOCKER_COMPOSE_FILES) ps $(services)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# interact
|
||||||
|
#
|
||||||
|
interact: it ## See `it'
|
||||||
|
it: ## Run a new container in interactive mode. Needs image name [image]
|
||||||
|
ifeq ($(image),)
|
||||||
|
$(error ERROR: 'image' is not set. Please provide 'image=' argument or edit makefile and set CONTAINER_DEFAULT)
|
||||||
|
endif
|
||||||
|
@echo
|
||||||
|
@echo "Starting interactive shell ($(SHELL_CMD)) in image container '$(image)'"
|
||||||
|
@$(DOCKER) run -it --entrypoint "$(SHELL_CMD)" $(image)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# bash
|
||||||
|
#
|
||||||
|
sh: bash ## See 'bash'
|
||||||
|
shell: bash ## See 'bash'
|
||||||
|
bash: ## Brings up a shell in default (or specified) container [container]
|
||||||
|
ifeq ($(container),)
|
||||||
|
$(error ERROR: 'container' is not set. Please provide 'container=' argument or edit makefile and set CONTAINER_DEFAULT)
|
||||||
|
endif
|
||||||
|
@echo
|
||||||
|
@echo "Starting shell ($(SHELL_CMD)) in container '$(container)'"
|
||||||
|
@$(DOCKER) exec -it "$(container)" "$(SHELL_CMD)"
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# attach
|
||||||
|
#
|
||||||
|
at: attach ## See 'attach'
|
||||||
|
attach: ## Attach to a running container [container]
|
||||||
|
ifeq ($(container),)
|
||||||
|
$(error ERROR: 'container' is not set. Please provide 'container=' argument or edit makefile and set CONTAINER_DEFAULT)
|
||||||
|
endif
|
||||||
|
@echo
|
||||||
|
@echo "Attaching to '$(container)'"
|
||||||
|
@$(DOCKER) attach $(container)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# log
|
||||||
|
#
|
||||||
|
log: ## Shows log from a specific container (in 'follow' mode) [container]
|
||||||
|
ifeq ($(container),)
|
||||||
|
$(error ERROR: 'container' is not set. Please provide 'container=' argument or edit makefile and set CONTAINER_DEFAULT)
|
||||||
|
endif
|
||||||
|
@echo
|
||||||
|
@echo "Log in $(container)"
|
||||||
|
@$(DOCKER) logs -f $(container)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# logs
|
||||||
|
#
|
||||||
|
logs: ## Shows output of running containers (in 'follow' mode) [service|services]
|
||||||
|
@echo
|
||||||
|
@echo "Logs in $(services)"
|
||||||
|
@$(DOCKER_COMPOSE) $(DOCKER_COMPOSE_FILES) logs -f $(services)
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# rmimages
|
||||||
|
#
|
||||||
|
rmimages: ## Remove images
|
||||||
|
@echo
|
||||||
|
@echo "Remove local images"
|
||||||
|
@$(DOCKER_COMPOSE) $(DOCKER_COMPOSE_FILES) down --rmi local
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# clean
|
||||||
|
#
|
||||||
|
clean: ## Remove containers, images and volumes
|
||||||
|
@echo
|
||||||
|
@echo "Remove containers, images and volumes"
|
||||||
|
@$(DOCKER_COMPOSE) $(DOCKER_COMPOSE_FILES) down --volumes --rmi all
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%:
|
||||||
|
@:
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: %
|
||||||
|
|
73
README.md
73
README.md
@ -1,6 +1,46 @@
|
|||||||
# Proyecto HeatingEvent
|
# Proyecto Heating Monitor
|
||||||
|
|
||||||
Este proyecto gestiona eventos relacionados con el encendido y apagado de sistemas de calefacción, ofreciendo tres funcionalidades principales: una aplicación web, un bot de Telegram y un comando para importar datos desde un archivo CSV a la base de datos.
|
Este proyecto gestiona eventos relacionados con el encendido y apagado de sistemas de calefacción, ofreciendo tres funcionalidades principales:
|
||||||
|
|
||||||
|
- Una aplicación web
|
||||||
|
- Un bot de Telegram
|
||||||
|
- Un comando para importar datos desde un archivo CSV a la base de datos.
|
||||||
|
|
||||||
|
## Configuración
|
||||||
|
|
||||||
|
1. Crear archivo .env
|
||||||
|
Copia el archivo .env.sample a .env y configura las variables de entorno necesarias:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ cp .env.sample .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Luego edita el archivo .env con los valores correspondientes:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# Telegram Bot Configuration
|
||||||
|
TELEGRAM_TOKEN=xxx:yyy
|
||||||
|
TELEGRAM_CHATID=123123123
|
||||||
|
# Web Server Configuration
|
||||||
|
WEB_PORT=9900
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Base de Datos
|
||||||
|
El proyecto utiliza una base de datos `SQLite` para almacenar los eventos de calefacción. La configuración de la base de datos se gestiona automáticamente a través del código en el archivo `internal/config/db.go`. Asegúrate de que el archivo de la base de datos se encuentra en la ubicación correcta para que la conexión funcione correctamente.
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
Este proyecto puede ejecutarse fácilmente con Docker. Se han configurado dos entornos: `local` y `production`. En el entorno `local`, se utiliza `Air` para recargar automáticamente el código durante el desarrollo, mientras que en `production` se compila el binario y se ejecuta directamente. Asegúrate de tener Docker y Docker compose instalados en tu sistema.
|
||||||
|
|
||||||
|
El `Makefile` ha sido configurado para facilitar la construcción y ejecución de Docker con el argumento `--build-arg`, que pasará la variable de entorno `DOCKER_ENV` para que determine el mismo. Dicha variable se lee del archivo `.env`:
|
||||||
|
|
||||||
|
`--build-arg DOCKER_ENV=${DOCKER_ENV}`: Utiliza esta variable para determinar el entorno de ejecución (`local` o `production`), lo que afecta tanto la construcción de los binarios como el comportamiento del contenedor.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ make build
|
||||||
|
$ make rebuild
|
||||||
|
$ make start
|
||||||
|
```
|
||||||
|
|
||||||
## Comandos disponibles
|
## Comandos disponibles
|
||||||
|
|
||||||
@ -9,6 +49,7 @@ Este proyecto gestiona eventos relacionados con el encendido y apagado de sistem
|
|||||||
Este comando inicia un servidor web que permite visualizar los eventos de calefacción a través de una interfaz web.
|
Este comando inicia un servidor web que permite visualizar los eventos de calefacción a través de una interfaz web.
|
||||||
|
|
||||||
#### Uso:
|
#### Uso:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ go run cmd/web/main.go
|
$ go run cmd/web/main.go
|
||||||
```
|
```
|
||||||
@ -18,9 +59,11 @@ $ go run cmd/web/main.go
|
|||||||
- WEB_PORT: Puerto en el que se ejecuta el servidor web. Puedes configurarlo en el archivo `.env`.
|
- WEB_PORT: Puerto en el que se ejecuta el servidor web. Puedes configurarlo en el archivo `.env`.
|
||||||
|
|
||||||
### 2. Comando Bot
|
### 2. Comando Bot
|
||||||
|
|
||||||
Este comando inicia un bot de Telegram que permite interactuar con el sistema de calefacción a través de mensajes.
|
Este comando inicia un bot de Telegram que permite interactuar con el sistema de calefacción a través de mensajes.
|
||||||
|
|
||||||
#### Uso:
|
#### Uso:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ go run cmd/bot/main.go
|
$ go run cmd/bot/main.go
|
||||||
```
|
```
|
||||||
@ -35,6 +78,7 @@ $ go run cmd/bot/main.go
|
|||||||
Este comando importa los datos de un archivo CSV (con formato específico) a la base de datos SQLite, creando eventos de encendido y apagado en función de la información proporcionada en el archivo.
|
Este comando importa los datos de un archivo CSV (con formato específico) a la base de datos SQLite, creando eventos de encendido y apagado en función de la información proporcionada en el archivo.
|
||||||
|
|
||||||
#### Uso:
|
#### Uso:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ go run cmd/import/main.go -f /ruta/al/archivo.csv
|
$ go run cmd/import/main.go -f /ruta/al/archivo.csv
|
||||||
-f, --file: Especifica la ruta del archivo CSV a importar.
|
-f, --file: Especifica la ruta del archivo CSV a importar.
|
||||||
@ -42,26 +86,7 @@ $ go run cmd/import/main.go -f /ruta/al/archivo.csv
|
|||||||
(útil para verificar la información antes de insertarla).
|
(útil para verificar la información antes de insertarla).
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Configuración
|
## Dependencias
|
||||||
|
|
||||||
1. Crear archivo .env
|
|
||||||
Copia el archivo .env.sample a .env y configura las variables de entorno necesarias:
|
|
||||||
```sh
|
|
||||||
$ cp .env.sample .env
|
|
||||||
```
|
|
||||||
Luego edita el archivo .env con los valores correspondientes:
|
|
||||||
```ini
|
|
||||||
# Telegram Bot Configuration
|
|
||||||
TELEGRAM_TOKEN=xxx:yyy
|
|
||||||
TELEGRAM_CHATID=123123123
|
|
||||||
# Web Server Configuration
|
|
||||||
WEB_PORT=9900
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Base de Datos
|
|
||||||
El proyecto utiliza una base de datos SQLite para almacenar los eventos de calefacción. La configuración de la base de datos se gestiona automáticamente a través del código en el archivo `internal/config/db.go`. Asegúrate de que el archivo de la base de datos se encuentra en la ubicación correcta para que la conexión funcione correctamente.
|
|
||||||
|
|
||||||
### Dependencias
|
|
||||||
|
|
||||||
Este proyecto utiliza varias dependencias de Go. Asegúrate de tenerlas instaladas y configuradas correctamente:
|
Este proyecto utiliza varias dependencias de Go. Asegúrate de tenerlas instaladas y configuradas correctamente:
|
||||||
|
|
||||||
@ -69,7 +94,7 @@ Este proyecto utiliza varias dependencias de Go. Asegúrate de tenerlas instalad
|
|||||||
$ go mod tidy
|
$ go mod tidy
|
||||||
```
|
```
|
||||||
|
|
||||||
### Estructura del Proyecto
|
## Estructura del Proyecto
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
.
|
.
|
||||||
@ -84,7 +109,7 @@ $ go mod tidy
|
|||||||
└── README.md # Este archivo
|
└── README.md # Este archivo
|
||||||
```
|
```
|
||||||
|
|
||||||
### Contribución
|
## Contribución
|
||||||
|
|
||||||
Si deseas contribuir a este proyecto, por favor sigue estos pasos:
|
Si deseas contribuir a este proyecto, por favor sigue estos pasos:
|
||||||
|
|
||||||
|
33
docker-compose.yml
Normal file
33
docker-compose.yml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
name: "heating-monitor"
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
bot:
|
||||||
|
container_name: bot
|
||||||
|
hostname: bot
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.bot
|
||||||
|
volumes:
|
||||||
|
- ./:/code/
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
web:
|
||||||
|
container_name: web
|
||||||
|
hostname: web
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.web
|
||||||
|
volumes:
|
||||||
|
- ./:/code/
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
ports:
|
||||||
|
- ${WEB_PORT}:${WEB_PORT}
|
||||||
|
restart: always
|
@ -1,10 +1,13 @@
|
|||||||
|
# Docker
|
||||||
|
DOCKER_ENV=local
|
||||||
|
PROJECT_NAME=heating-monitor
|
||||||
|
|
||||||
# Telegram
|
# Telegram
|
||||||
TELEGRAM_TOKEN=aaaa:bbbb
|
TELEGRAM_TOKEN=aaaa:bbbb
|
||||||
TELEGRAM_CHATID=123123
|
TELEGRAM_CHATID=123123
|
||||||
|
|
||||||
# Web
|
# Web
|
||||||
WEB_PORT=9900
|
WEB_PORT=9900
|
||||||
|
|
||||||
PROGRAM_AUTHOR="oscarmlage"
|
PROGRAM_AUTHOR="oscarmlage"
|
||||||
PROGRAM_LINK="https://oscarmlage.com"
|
PROGRAM_LINK="https://oscarmlage.com"
|
||||||
PROGRAM_AVATAR="https://mastodon.bofhers.es/system/accounts/avatars/108/369/580/175/949/602/original/01f8e5e7fcc34119.jpg"
|
PROGRAM_AVATAR="https://mastodon.bofhers.es/system/accounts/avatars/108/369/580/175/949/602/original/01f8e5e7fcc34119.jpg"
|
||||||
|
Loading…
Reference in New Issue
Block a user