Home / Developers / Metin2
🐉

Metin2

Python Quest Scripts (Python)

Overview

Reward your Metin2 private server players for voting on topgservers. This guide uses Python quest/event scripts to poll the vote-check API and a lightweight Python sidecar for real-time webhook rewards. Players receive yang, items, or EXP automatically after voting.

Prerequisites

  • A Metin2 private server with Python quest support (most source releases)
  • Python 2.7 or 3.x available on the server machine
  • A topgservers API key — generate one in My Servers → API
  • Your webhook secret (optional, for real-time rewards)
  • Port 443 outbound access from your server for HTTPS calls

Installation

1 Create the quest script

Metin2 private servers use Python-based quest scripts. Place this in your `quest/` directory or load it through your server's quest system.

Directory structure
share/locale/quest/
├── topg_vote.quest
└── topg_vote_check.py

2 Write the quest definition

The quest definition triggers on login and periodically checks vote status for the player.

topg_vote.quest
quest topg_vote begin
    state start begin
        when login begin
            timer("topg_check", 60)
        end

        when timer("topg_check") begin
            local result = topg_vote_check(pc.get_name())
            if result == 1 then
                -- Grant rewards
                pc.give_item2(27001, 5)    -- Red Potion (L) x5
                pc.changegold(500000)       -- 500k yang
                syschat("Thanks for voting on topgservers! Rewards granted.")
            end
            timer("topg_check", 60)
        end
    end
end

3 Create the vote check script

This Python script is called from the quest to check vote status via the API.

topg_vote_check.py
"""
Vote check helper for Metin2 quest system.
Called from topg_vote.quest to verify player votes.
"""
import urllib2  # Python 2 (common in Metin2 sources)
import json

API_KEY = "tgs_your_api_key_here"
API_URL = "https://topgservers.net/api/v1/vote-check"

def topg_vote_check(player_name):
    """Returns 1 if player voted today, 0 otherwise."""
    try:
        req = urllib2.Request(
            API_URL + "?username=" + player_name,
            headers={"Authorization": "Bearer " + API_KEY}
        )
        response = urllib2.urlopen(req, timeout=5)
        data = json.loads(response.read())
        return 1 if data.get("voted") else 0
    except Exception:
        return 0

4 Alternative: Python 3 vote checker (standalone)

If your server supports Python 3 or you prefer a standalone cron-based approach, use this script to write vote results to a database table.

topg_cron_check.py
#!/usr/bin/env python3
"""
Standalone vote checker for Metin2 servers.
Run via cron: * * * * * python3 /path/to/topg_cron_check.py
"""
import requests
import mysql.connector

API_KEY = "tgs_your_api_key_here"
DB_CONFIG = {
    "host": "127.0.0.1",
    "user": "metin2",
    "password": "password",
    "database": "player"
}

def check_online_players():
    db = mysql.connector.connect(**DB_CONFIG)
    cursor = db.cursor()

    # Get online players
    cursor.execute("SELECT name FROM player WHERE online = 1")
    players = [row[0] for row in cursor.fetchall()]

    for name in players:
        try:
            resp = requests.get(
                f"https://topgservers.net/api/v1/vote-check?username={name}",
                headers={"Authorization": f"Bearer {API_KEY}"},
                timeout=5
            )
            if resp.status_code == 200 and resp.json().get("voted"):
                cursor.execute(
                    "INSERT IGNORE INTO topg_votes (name, date) "
                    "VALUES (%s, CURDATE())",
                    (name,)
                )
        except Exception:
            continue

    db.commit()
    db.close()

if __name__ == "__main__":
    check_online_players()

Configuration

topg_vote_check.py (config section)
# Configuration
API_KEY         = "tgs_your_api_key_here"
WEBHOOK_SECRET  = "whsec_your_secret_here"
API_URL         = "https://topgservers.net/api/v1/vote-check"

# Rewards
REWARD_ITEM     = 27001      # Red Potion (L)
REWARD_COUNT    = 5
REWARD_YANG     = 500000     # 500k yang
REWARD_EXP      = 0          # bonus EXP (0 = disabled)

# Cooldown
CHECK_INTERVAL  = 60         # seconds between checks

Vote Check

Call the /api/v1/vote-check endpoint to determine if a player has voted today.

Vote check (Python 3)
import requests

resp = requests.get(
    "https://topgservers.net/api/v1/vote-check?username=" + player_name,
    headers={"Authorization": "Bearer " + API_KEY},
    timeout=5
)

if resp.status_code == 200 and resp.json().get("voted"):
    # Player voted today — grant reward
    pass

Webhook Receiver

Verify the X-TopG-Signature header using HMAC-SHA256 to ensure webhook payloads are authentic.

webhook_receiver.py (sidecar)
"""
Webhook receiver for Metin2 servers.
Run alongside your game server. Inserts into topg_votes table
so the quest script can pick up rewards on next check.
"""
import hmac, hashlib, json, mysql.connector
from http.server import HTTPServer, BaseHTTPRequestHandler

WEBHOOK_SECRET = "whsec_your_secret_here"
DB_CONFIG = {
    "host": "127.0.0.1",
    "user": "metin2",
    "password": "password",
    "database": "player"
}

class WebhookHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get('Content-Length', 0))
        body = self.rfile.read(length).decode()
        sig = self.headers.get('X-TopG-Signature', '')

        if not verify_signature(body, sig):
            self.send_response(401)
            self.end_headers()
            return

        data = json.loads(body)
        username = data.get('username', '')

        if username:
            db = mysql.connector.connect(**DB_CONFIG)
            cursor = db.cursor()
            cursor.execute(
                "INSERT IGNORE INTO topg_votes (name, date) "
                "VALUES (%s, CURDATE())",
                (username,)
            )
            db.commit()
            db.close()

        self.send_response(200)
        self.end_headers()

def verify_signature(body, sig_header):
    parts = dict(p.split('=', 1) for p in sig_header.split(',') if '=' in p)
    timestamp = parts.get('t', '')
    received = parts.get('v1', '')

    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        f"{timestamp}.{body}".encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected, received)

if __name__ == '__main__':
    server = HTTPServer(('0.0.0.0', 8090), WebhookHandler)
    print("Webhook receiver listening on :8090")
    server.serve_forever()

Reward Examples

Reward examples (quest script)
# Items — give item by VNUM
pc.give_item2(27001, 5)         # Red Potion (L) x5
pc.give_item2(50300, 1)         # Skill Reset Book

# Yang (gold)
pc.changegold(500000)           # +500k yang

# EXP
pc.give_exp2(100000)            # +100k EXP

# Buff / status
affect.add(apply.ATT_SPEED, 30, 3600)  # +30% attack speed for 1hr

# Custom announcement
notice_all("Player " .. pc.get_name() .. " voted on topgservers!")

Notes & Tips

Most Metin2 private servers run Python 2.7 in the quest engine. The standalone cron approach (Python 3 + requests) is recommended for reliability. Create the votes table with: `CREATE TABLE topg_votes (name VARCHAR(50), date DATE, rewarded TINYINT DEFAULT 0, PRIMARY KEY(name, date));` The quest then reads this table on login and timer events.