Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Python Script to automatically add Servers to DiscordGSM #129

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions ADD_SERVER_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# DiscordGSM Server Automation Script

Command-line tool to automate adding game servers to DiscordGSM's SQLite database.

## Requirements

- Python 3.6+
- SQLite3
- Access to DiscordGSM's `servers.db` file
- **Important**: Must run in DiscordGSM's virtual environment
```bash
source /path/to/discordgsm/venv/bin/activate # Linux/Mac
# or
\path\to\discordgsm\venv\Scripts\activate # Windows
```

## Installation

1. Save `add_server.py` to your desired location
2. Ensure you have write access to DiscordGSM's database directory

## Usage

### Command Line

```bash
python3 add_server.py \
--guild_id YOUR_GUILD_ID \
--channel_id YOUR_CHANNEL_ID \
--game_id GAME_ID \
--address SERVER_ADDRESS \
--query_port QUERY_PORT \
[--db_path PATH_TO_DB] \
[--ignore-existing]
```

### Arguments

Required:
- `--guild_id`: Discord server (guild) ID
- `--channel_id`: Discord channel ID
- `--game_id`: Game identifier (e.g., "minecraft", "valheim")
- `--address`: Server address/hostname
- `--query_port`: Query port number

Optional:
- `--db_path`: Custom path to `servers.db` (defaults to `./data/servers.db`)
- `--ignore-existing`: Continue execution if server already exists

## Examples

Basic usage:
```bash
python3 add_server.py \
--guild_id 123456789 \
--channel_id 987654321 \
--game_id minecraft \
--address mc.example.com \
--query_port 25565
```

Ignoring existing servers:
```bash
python3 add_server.py \
--guild_id 123456789 \
--channel_id 987654321 \
--game_id valheim \
--address valheim.example.com \
--query_port 2457 \
--ignore-existing
```

## Error Handling

- Validates database existence
- Checks for existing servers with same address and query port
- Provides detailed error messages including channel and game info for existing servers
- Returns exit code 1 on error unless `--ignore-existing` is set

## Ansible Integration

Use `add_servers.yml` for automated deployment:

```yaml
vars:
ignore_existing: true # Set to false to fail on existing servers
```

Key features:
- Database existence validation
- Optional failure on existing servers (`ignore_existing`)
- Detailed output summary
- Proper idempotency handling
151 changes: 151 additions & 0 deletions discordgsm/add_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import sqlite3
import json
from typing import Optional, Dict, Tuple
from dataclasses import dataclass
import os
from pathlib import Path
import argparse
import sys

@dataclass
class ServerConfig:
guild_id: int
channel_id: int
game_id: str
address: str
query_port: int
query_extra: Dict = None
style_id: str = "Medium"
style_data: Dict = None
position: Optional[int] = None
status: bool = False
result: Dict = None
message_id: Optional[int] = None

class DGSMAutomation:
def __init__(self, db_path: str = None):
if db_path is None:
db_path = os.path.join(Path(__file__).parent, "data", "servers.db")
self.db_path = db_path
if not os.path.exists(self.db_path):
raise FileNotFoundError(f"Database not found at {self.db_path}")
self._init_db()

def _init_db(self):
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS servers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
position INT NOT NULL,
guild_id BIGINT NOT NULL,
channel_id BIGINT NOT NULL,
message_id BIGINT,
game_id TEXT NOT NULL,
address TEXT NOT NULL,
query_port INT(5) NOT NULL,
query_extra TEXT NOT NULL,
status INT(1) NOT NULL,
result TEXT NOT NULL,
style_id TEXT NOT NULL,
style_data TEXT NOT NULL
)
''')
conn.commit()

def server_exists(self, address: str, query_port: int) -> Tuple[bool, Optional[Dict]]:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute(
'SELECT channel_id, game_id FROM servers WHERE address = ? AND query_port = ?',
(address, query_port)
)
result = cursor.fetchone()
if result:
return True, {
'channel_id': result[0],
'game_id': result[1]
}
return False, None

def add_server(self, config: ServerConfig) -> Tuple[bool, str]:
try:
exists, existing_data = self.server_exists(config.address, config.query_port)
if exists:
return False, f"Server {config.address}:{config.query_port} already exists in channel {existing_data['channel_id']} for game {existing_data['game_id']}"

with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()

if config.position is None:
cursor.execute(
'SELECT COALESCE(MAX(position + 1), 0) FROM servers WHERE channel_id = ?',
(config.channel_id,)
)
config.position = cursor.fetchone()[0]

config.query_extra = config.query_extra or {}
config.style_data = config.style_data or {"locale": "en-US", "timezone": "UTC"}
config.result = config.result or {"name": "", "map": "", "password": False, "raw": {}, "connect": "", "numplayers": 0, "numbots": 0, "maxplayers": 0, "players": [], "bots": []}

cursor.execute('''
INSERT INTO servers (
position, guild_id, channel_id, game_id, address, query_port,
query_extra, status, result, style_id, style_data, message_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
config.position,
config.guild_id,
config.channel_id,
config.game_id,
config.address,
config.query_port,
json.dumps(config.query_extra),
int(config.status),
json.dumps(config.result),
config.style_id,
json.dumps(config.style_data),
config.message_id
))
conn.commit()
return True, f"Successfully added server {config.address}:{config.query_port}"
except sqlite3.Error as e:
return False, f"Database error: {str(e)}"
except Exception as e:
return False, f"Unexpected error: {str(e)}"

def main():
parser = argparse.ArgumentParser(description='Add server to DiscordGSM')
parser.add_argument('--guild_id', type=int, required=True)
parser.add_argument('--channel_id', type=int, required=True)
parser.add_argument('--game_id', type=str, required=True)
parser.add_argument('--address', type=str, required=True)
parser.add_argument('--query_port', type=int, required=True)
parser.add_argument('--db_path', type=str, help='Path to servers.db')
parser.add_argument('--ignore-existing', action='store_true', help='Continue if server exists')

args = parser.parse_args()

try:
automation = DGSMAutomation(db_path=args.db_path)

server_config = ServerConfig(
guild_id=args.guild_id,
channel_id=args.channel_id,
game_id=args.game_id,
address=args.address,
query_port=args.query_port
)

success, message = automation.add_server(server_config)
print(message)

if not success and not args.ignore_existing:
sys.exit(1)

except Exception as e:
print(f"Error: {str(e)}")
sys.exit(1)

if __name__ == "__main__":
main()
43 changes: 43 additions & 0 deletions discordgsm/add_servers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
- name: Add servers to DiscordGSM
hosts: localhost
gather_facts: no

vars:
discordgsm_db: "/path/to/servers.db"
ignore_existing: true # Set to false to fail on existing servers
servers:
- guild_id: 123456789
channel_id: 987654321
game_id: "minecraft"
address: "mc.example.com"
query_port: 25565
# Add more servers as needed

tasks:
- name: Ensure DiscordGSM database exists
stat:
path: "{{ discordgsm_db }}"
register: db_check
failed_when: not db_check.stat.exists

- name: Add server to DiscordGSM
command: >
python3 add_server.py
--guild_id {{ item.guild_id }}
--channel_id {{ item.channel_id }}
--game_id {{ item.game_id }}
--address {{ item.address }}
--query_port {{ item.query_port }}
--db_path {{ discordgsm_db }}
{% if ignore_existing %}--ignore-existing{% endif %}
register: result
failed_when:
- result.rc != 0
- not ignore_existing
changed_when: "'Successfully added server' in result.stdout"
with_items: "{{ servers }}"

- name: Show results
debug:
msg: "{{ result.results | map(attribute='stdout_lines') | list }}"