Fix HTTPS to HTTP conversion for Gitea API
This commit is contained in:
parent
edb409bb0c
commit
7db96541a6
@ -101,6 +101,15 @@ Your workspace is at: {workspace_path}
|
||||
- History log: {workspace_path}/memory/HISTORY.md (grep-searchable)
|
||||
- Custom skills: {workspace_path}/skills/{{skill-name}}/SKILL.md
|
||||
|
||||
## Gitea API (This Repository)
|
||||
**CRITICAL**: This repository uses Gitea at `http://10.0.30.169:3000/api/v1`, NOT GitHub.
|
||||
- Repository: `ilia/nanobot`
|
||||
- Token: `$NANOBOT_GITLE_TOKEN`
|
||||
- **NEVER use placeholder URLs like `gitea.example.com`**
|
||||
- **ALWAYS use `http://` (NOT `https://`)** - Gitea runs on HTTP, using HTTPS causes SSL errors
|
||||
- Always detect from `git remote get-url origin` or use `http://10.0.30.169:3000/api/v1`
|
||||
- Example: `curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" "http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"`
|
||||
|
||||
IMPORTANT: When responding to direct questions or conversations, reply directly with your text response.
|
||||
Only use the 'message' tool when the user explicitly asks you to send a message to someone else or to a different channel.
|
||||
For normal conversation, acknowledgments (Thanks, OK, etc.), or when the user is talking to YOU, just respond with text - do NOT call the message tool.
|
||||
|
||||
@ -74,6 +74,10 @@ For data analysis tasks (Excel, CSV, JSON files), use Python with pandas:
|
||||
|
||||
async def execute(self, command: str, working_dir: str | None = None, **kwargs: Any) -> str:
|
||||
cwd = working_dir or self.working_dir or os.getcwd()
|
||||
|
||||
# Sanitize Gitea API URLs: convert HTTPS to HTTP for 10.0.30.169:3000
|
||||
command = self._sanitize_gitea_urls(command)
|
||||
|
||||
guard_error = self._guard_command(command, cwd)
|
||||
if guard_error:
|
||||
return guard_error
|
||||
@ -83,11 +87,14 @@ For data analysis tasks (Excel, CSV, JSON files), use Python with pandas:
|
||||
logger.debug(f"ExecTool: command={command[:200]}, cwd={cwd}, working_dir={working_dir}")
|
||||
|
||||
try:
|
||||
# Ensure environment variables are available (including from .env file)
|
||||
env = os.environ.copy()
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
command,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
cwd=cwd,
|
||||
env=env,
|
||||
)
|
||||
|
||||
try:
|
||||
@ -200,3 +207,33 @@ For data analysis tasks (Excel, CSV, JSON files), use Python with pandas:
|
||||
return "Error: Command blocked by safety guard (path outside working dir)"
|
||||
|
||||
return None
|
||||
|
||||
def _sanitize_gitea_urls(self, command: str) -> str:
|
||||
"""
|
||||
Sanitize Gitea API URLs in curl commands: convert HTTPS to HTTP.
|
||||
|
||||
Gitea API at 10.0.30.169:3000 runs on HTTP, not HTTPS.
|
||||
This prevents SSL/TLS errors when the agent generates HTTPS URLs.
|
||||
"""
|
||||
# Pattern to match https://10.0.30.169:3000/api/... in curl commands
|
||||
# This handles various curl formats:
|
||||
# - curl "https://10.0.30.169:3000/api/..."
|
||||
# - curl -X GET https://10.0.30.169:3000/api/...
|
||||
# - curl -H "..." "https://10.0.30.169:3000/api/..."
|
||||
# Matches URLs with or without quotes, and captures the full path
|
||||
pattern = r'https://10\.0\.30\.169:3000(/api/[^\s"\']*)'
|
||||
|
||||
def replace_url(match):
|
||||
path = match.group(1)
|
||||
return f'http://10.0.30.169:3000{path}'
|
||||
|
||||
sanitized = re.sub(pattern, replace_url, command)
|
||||
|
||||
# Log if we made a change
|
||||
if sanitized != command:
|
||||
from loguru import logger
|
||||
logger.info(f"ExecTool: Sanitized Gitea API URL (HTTPS -> HTTP)")
|
||||
logger.debug(f"Original: {command[:200]}...")
|
||||
logger.debug(f"Sanitized: {sanitized[:200]}...")
|
||||
|
||||
return sanitized
|
||||
|
||||
@ -517,6 +517,7 @@ def agent(
|
||||
from nanobot.cron.service import CronService
|
||||
from loguru import logger
|
||||
|
||||
# Load config (this also loads .env file into environment)
|
||||
config = load_config()
|
||||
|
||||
bus = MessageBus()
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
"""Configuration loading utilities."""
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from nanobot.config.schema import Config
|
||||
@ -17,6 +18,43 @@ def get_data_dir() -> Path:
|
||||
return get_data_path()
|
||||
|
||||
|
||||
def _load_env_file(workspace: Path | None = None) -> None:
|
||||
"""Load .env file from workspace directory if it exists."""
|
||||
if workspace:
|
||||
env_file = Path(workspace) / ".env"
|
||||
else:
|
||||
# Try current directory and workspace
|
||||
env_file = Path(".env")
|
||||
if not env_file.exists():
|
||||
# Try workspace directory
|
||||
try:
|
||||
from nanobot.utils.helpers import get_workspace_path
|
||||
workspace_path = get_workspace_path()
|
||||
env_file = workspace_path / ".env"
|
||||
except:
|
||||
pass
|
||||
|
||||
if env_file.exists():
|
||||
try:
|
||||
with open(env_file) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
# Skip comments and empty lines
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
# Parse KEY=VALUE format
|
||||
if "=" in line:
|
||||
key, value = line.split("=", 1)
|
||||
key = key.strip()
|
||||
value = value.strip().strip('"').strip("'")
|
||||
# Only set if not already in environment
|
||||
if key and key not in os.environ:
|
||||
os.environ[key] = value
|
||||
except Exception:
|
||||
# Silently fail if .env can't be loaded
|
||||
pass
|
||||
|
||||
|
||||
def load_config(config_path: Path | None = None) -> Config:
|
||||
"""
|
||||
Load configuration from file or create default.
|
||||
@ -27,6 +65,15 @@ def load_config(config_path: Path | None = None) -> Config:
|
||||
Returns:
|
||||
Loaded configuration object.
|
||||
"""
|
||||
# Load .env file before loading config (so env vars are available to pydantic)
|
||||
try:
|
||||
from nanobot.utils.helpers import get_workspace_path
|
||||
workspace = get_workspace_path()
|
||||
_load_env_file(workspace)
|
||||
except:
|
||||
# Fallback to current directory
|
||||
_load_env_file()
|
||||
|
||||
path = config_path or get_config_path()
|
||||
|
||||
if path.exists():
|
||||
|
||||
52
nanobot/skills/gitea/SKILL.md
Normal file
52
nanobot/skills/gitea/SKILL.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
name: gitea
|
||||
description: "Interact with Gitea API using curl. This repository uses Gitea (NOT GitHub) at http://10.0.30.169:3000/api/v1. ALWAYS use HTTP (not HTTPS)."
|
||||
metadata: {"nanobot":{"emoji":"🔧","requires":{"env":["NANOBOT_GITLE_TOKEN"]}}}
|
||||
---
|
||||
|
||||
# Gitea Skill
|
||||
|
||||
**CRITICAL**: This repository uses Gitea at `http://10.0.30.169:3000/api/v1`, NOT GitHub.
|
||||
|
||||
## Important Rules
|
||||
|
||||
1. **ALWAYS use `http://` (NOT `https://`)** - Gitea runs on HTTP port 3000
|
||||
2. **ALWAYS include Authorization header** with `$NANOBOT_GITLE_TOKEN`
|
||||
3. **Repository**: `ilia/nanobot`
|
||||
4. **API Base**: `http://10.0.30.169:3000/api/v1`
|
||||
|
||||
## Pull Requests
|
||||
|
||||
List all pull requests:
|
||||
```bash
|
||||
curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"
|
||||
```
|
||||
|
||||
List open pull requests:
|
||||
```bash
|
||||
curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls?state=open"
|
||||
```
|
||||
|
||||
## Issues
|
||||
|
||||
List open issues:
|
||||
```bash
|
||||
curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/issues?state=open"
|
||||
```
|
||||
|
||||
## Helper Script
|
||||
|
||||
You can also use the helper script:
|
||||
```bash
|
||||
./workspace/gitea_api.sh prs
|
||||
./workspace/gitea_api.sh issues open
|
||||
```
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
❌ **WRONG**: `curl https://10.0.30.169:3000/api/...` (SSL error)
|
||||
❌ **WRONG**: `curl http://gitea.example.com/api/...` (placeholder URL)
|
||||
✅ **CORRECT**: `curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" "http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"`
|
||||
@ -2,6 +2,34 @@
|
||||
|
||||
You are a helpful AI assistant. Be concise, accurate, and friendly.
|
||||
|
||||
## 🚨 CRITICAL: Gitea API Requests
|
||||
|
||||
**When user asks to list PRs, issues, or use Gitea API:**
|
||||
|
||||
**MANDATORY COMMAND FORMAT:**
|
||||
```bash
|
||||
curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" "http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"
|
||||
```
|
||||
|
||||
**CRITICAL RULES:**
|
||||
1. **DO NOT use web_search** - execute the curl command directly
|
||||
2. **MUST use `http://` (NOT `https://`)** - Gitea runs on HTTP port 3000
|
||||
3. **MUST include Authorization header** with `$NANOBOT_GITLE_TOKEN`
|
||||
4. **Copy the exact command above** - do not modify the protocol to HTTPS
|
||||
|
||||
**WRONG (will fail):**
|
||||
- `curl -X GET https://10.0.30.169:3000/api/...` ❌ (SSL error)
|
||||
- `curl https://10.0.30.169:3000/api/...` ❌ (SSL error)
|
||||
|
||||
**CORRECT:**
|
||||
- `curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" "http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"` ✅
|
||||
|
||||
**OR use the helper script (recommended - avoids HTTPS mistakes):**
|
||||
```bash
|
||||
./workspace/gitea_api.sh prs
|
||||
./workspace/gitea_api.sh issues open
|
||||
```
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Always explain what you're doing before taking actions
|
||||
@ -88,3 +116,114 @@ Task format examples:
|
||||
```
|
||||
|
||||
When the user asks you to add a recurring/periodic task, update `HEARTBEAT.md` instead of creating a one-time reminder. Keep the file small to minimize token usage.
|
||||
|
||||
## ⚠️ CRITICAL: Gitea API Access
|
||||
|
||||
**THIS REPOSITORY USES GITEA, NOT GITHUB. NEVER USE PLACEHOLDER URLS.**
|
||||
|
||||
When user asks about pull requests, issues, or Gitea API:
|
||||
1. **ALWAYS detect the real Gitea URL from git remote first**
|
||||
2. **NEVER use placeholder URLs like `gitea.example.com` or `https://gitea.example.com`**
|
||||
3. **The correct Gitea API base is: `http://10.0.30.169:3000/api/v1`**
|
||||
|
||||
To access Gitea API:
|
||||
|
||||
1. **Detect Gitea URL from git remote:**
|
||||
```bash
|
||||
git remote get-url origin
|
||||
# Returns: gitea@10.0.30.169:ilia/nanobot.git
|
||||
# Extract host: 10.0.30.169
|
||||
# API base: http://10.0.30.169:3000/api/v1
|
||||
# Repo: ilia/nanobot
|
||||
```
|
||||
|
||||
2. **Use the token from environment:**
|
||||
```bash
|
||||
TOKEN=$NANOBOT_GITLE_TOKEN
|
||||
curl -H "Authorization: token $TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"
|
||||
```
|
||||
|
||||
3. **Or use the helper script:**
|
||||
```bash
|
||||
source workspace/get_gitea_info.sh
|
||||
curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" \
|
||||
"${GITEA_API_BASE}/repos/${GITEA_REPO}/pulls"
|
||||
```
|
||||
|
||||
**Important:** Never use placeholder URLs like `gitea.example.com`. Always detect from git remote or use the actual host `10.0.30.169:3000`.
|
||||
|
||||
## 🚨 GITEA URL DETECTION (MANDATORY)
|
||||
|
||||
**BEFORE making any Gitea API call, you MUST:**
|
||||
|
||||
1. Run: `git remote get-url origin`
|
||||
- This returns: `gitea@10.0.30.169:ilia/nanobot.git`
|
||||
|
||||
2. Extract the host: `10.0.30.169`
|
||||
- Command: `git remote get-url origin | sed 's/.*@\([^:]*\).*/\1/'`
|
||||
|
||||
3. Extract the repo: `ilia/nanobot`
|
||||
- Command: `git remote get-url origin | sed 's/.*:\(.*\)\.git/\1/'`
|
||||
|
||||
4. Construct API URL: `http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/...`
|
||||
|
||||
**Example correct command (MUST use $NANOBOT_GITLE_TOKEN variable):**
|
||||
```bash
|
||||
curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"
|
||||
```
|
||||
|
||||
**CRITICAL**: Always use `$NANOBOT_GITLE_TOKEN` in the curl command. The token is automatically loaded from `.env` file into the environment when nanobot starts. Do NOT hardcode the token value.
|
||||
|
||||
**WRONG (never use):**
|
||||
- `https://gitea.example.com/api/...` ❌
|
||||
- `https://gitea.example.com/ap...` ❌
|
||||
- Any placeholder URL ❌
|
||||
|
||||
## Gitea API Token Usage
|
||||
|
||||
**MANDATORY**: When making Gitea API calls, you **MUST** include the Authorization header with the token:
|
||||
|
||||
```bash
|
||||
# ✅ CORRECT - includes Authorization header with token
|
||||
curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"
|
||||
|
||||
# ❌ WRONG - missing Authorization header (will get 401 error)
|
||||
curl -X GET "http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"
|
||||
|
||||
# ❌ WRONG - missing token in header
|
||||
curl "http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"
|
||||
```
|
||||
|
||||
**CRITICAL RULES:**
|
||||
1. **ALWAYS** include `-H "Authorization: token $NANOBOT_GITLE_TOKEN"` in curl commands
|
||||
2. **ALWAYS use `http://` (NOT `https://`)** - Gitea runs on HTTP, not HTTPS
|
||||
3. The token is automatically loaded from `.env` file when nanobot starts
|
||||
4. The `$NANOBOT_GITLE_TOKEN` environment variable is available in all exec commands
|
||||
5. Without the Authorization header, you will get a 401 Unauthorized error
|
||||
6. Using `https://` will cause SSL/TLS errors - always use `http://10.0.30.169:3000`
|
||||
|
||||
## Direct Gitea API Execution
|
||||
|
||||
When user asks to "list PRs" or "show issues" using Gitea API:
|
||||
|
||||
**DO THIS:**
|
||||
1. Run the curl command directly (no web_search needed)
|
||||
2. Use the exact command format below
|
||||
3. Parse and display the results
|
||||
|
||||
**Example - List PRs:**
|
||||
```bash
|
||||
curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls" | \
|
||||
python3 -m json.tool
|
||||
```
|
||||
|
||||
**DO NOT:**
|
||||
- Use web_search to find API documentation
|
||||
- Search for "how to use Gitea API"
|
||||
- Ask for clarification - just execute the command
|
||||
|
||||
You already have all the information you need in this file. Just execute the curl command.
|
||||
|
||||
46
workspace/GITEA_API.md
Normal file
46
workspace/GITEA_API.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Gitea API Quick Reference
|
||||
|
||||
**CRITICAL: This repository uses Gitea, NOT GitHub. Never use placeholder URLs.**
|
||||
|
||||
## Correct Gitea API Information
|
||||
|
||||
- **API Base URL**: `http://10.0.30.169:3000/api/v1`
|
||||
- **Repository**: `ilia/nanobot`
|
||||
- **Token**: Available in `$NANOBOT_GITLE_TOKEN` environment variable
|
||||
|
||||
## How to Detect (if needed)
|
||||
|
||||
```bash
|
||||
# Get git remote
|
||||
REMOTE=$(git remote get-url origin)
|
||||
# Returns: gitea@10.0.30.169:ilia/nanobot.git
|
||||
|
||||
# Extract host (remove gitea@ and :repo.git)
|
||||
HOST=$(echo "$REMOTE" | sed 's/.*@\([^:]*\).*/\1/')
|
||||
# Returns: 10.0.30.169
|
||||
|
||||
# Extract repo path
|
||||
REPO=$(echo "$REMOTE" | sed 's/.*:\(.*\)\.git/\1/')
|
||||
# Returns: ilia/nanobot
|
||||
|
||||
# API base (Gitea runs on port 3000)
|
||||
API_BASE="http://${HOST}:3000/api/v1"
|
||||
```
|
||||
|
||||
## Example API Calls
|
||||
|
||||
```bash
|
||||
# List pull requests
|
||||
curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"
|
||||
|
||||
# List open issues
|
||||
curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/issues?state=open"
|
||||
|
||||
# Get repository info
|
||||
curl -H "Authorization: token $NANOBOT_GITLE_TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot"
|
||||
```
|
||||
|
||||
**DO NOT USE**: `gitea.example.com` or any placeholder URLs. Always use `10.0.30.169:3000`.
|
||||
38
workspace/GITEA_INFO.md
Normal file
38
workspace/GITEA_INFO.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Gitea Configuration
|
||||
|
||||
## API Information
|
||||
|
||||
- **Gitea API Base URL**: `http://10.0.30.169:3000/api/v1`
|
||||
- **Repository**: `ilia/nanobot`
|
||||
- **Token Environment Variable**: `NANOBOT_GITLE_TOKEN`
|
||||
|
||||
## How to Use
|
||||
|
||||
When making Gitea API calls, use:
|
||||
|
||||
```bash
|
||||
# Get token from environment
|
||||
TOKEN=$NANOBOT_GITLE_TOKEN
|
||||
|
||||
# List open issues
|
||||
curl -H "Authorization: token $TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/issues?state=open"
|
||||
|
||||
# List pull requests
|
||||
curl -H "Authorization: token $TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot/pulls"
|
||||
|
||||
# Get repository info
|
||||
curl -H "Authorization: token $TOKEN" \
|
||||
"http://10.0.30.169:3000/api/v1/repos/ilia/nanobot"
|
||||
```
|
||||
|
||||
## Detecting Repository Info
|
||||
|
||||
You can detect the repository from git remote:
|
||||
```bash
|
||||
# Get repo path (owner/repo)
|
||||
git remote get-url origin | sed 's/.*:\(.*\)\.git/\1/'
|
||||
|
||||
# Gitea host is: 10.0.30.169:3000
|
||||
# API base: http://10.0.30.169:3000/api/v1
|
||||
25
workspace/get_gitea_info.sh
Executable file
25
workspace/get_gitea_info.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# Helper script to get Gitea API information from git remote
|
||||
|
||||
REMOTE=$(git remote get-url origin 2>/dev/null)
|
||||
if [ -z "$REMOTE" ]; then
|
||||
echo "Error: No git remote found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract host (assuming format: gitea@HOST:repo.git or ssh://gitea@HOST/repo.git)
|
||||
if [[ $REMOTE == *"@"* ]]; then
|
||||
HOST=$(echo "$REMOTE" | sed 's/.*@\([^:]*\).*/\1/')
|
||||
else
|
||||
HOST=$(echo "$REMOTE" | sed 's|.*://\([^/]*\).*|\1|')
|
||||
fi
|
||||
|
||||
# Extract repo path (owner/repo)
|
||||
REPO=$(echo "$REMOTE" | sed 's/.*:\(.*\)\.git/\1/' | sed 's|.*/\(.*/.*\)|\1|')
|
||||
|
||||
# Gitea typically runs on port 3000
|
||||
API_BASE="http://${HOST}:3000/api/v1"
|
||||
|
||||
echo "GITEA_HOST=${HOST}"
|
||||
echo "GITEA_REPO=${REPO}"
|
||||
echo "GITEA_API_BASE=${API_BASE}"
|
||||
28
workspace/gitea_api.sh
Executable file
28
workspace/gitea_api.sh
Executable file
@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# Gitea API helper script - ALWAYS uses HTTP (not HTTPS)
|
||||
|
||||
API_BASE="http://10.0.30.169:3000/api/v1"
|
||||
REPO="ilia/nanobot"
|
||||
TOKEN="${NANOBOT_GITLE_TOKEN}"
|
||||
|
||||
if [ -z "$TOKEN" ]; then
|
||||
echo "Error: NANOBOT_GITLE_TOKEN not set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
prs|pulls)
|
||||
curl -s -H "Authorization: token $TOKEN" \
|
||||
"${API_BASE}/repos/${REPO}/pulls"
|
||||
;;
|
||||
issues)
|
||||
curl -s -H "Authorization: token $TOKEN" \
|
||||
"${API_BASE}/repos/${REPO}/issues?state=${2:-open}"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {prs|pulls|issues} [state]"
|
||||
echo "Example: $0 prs"
|
||||
echo "Example: $0 issues open"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Loading…
x
Reference in New Issue
Block a user