Procházet zdrojové kódy

Merge master into develop

Daniel Gibbs před 1 měsícem
rodič
revize
58a005bbdd

+ 80 - 1
.github/ISSUE_TEMPLATE/bug_report.yml

@@ -8,6 +8,51 @@ body:
     attributes:
       value: |
         Thanks for taking the time to fill out this bug report!
+  - type: dropdown
+    id: severity
+    attributes:
+      label: Severity
+      description: Triage metadata used for prioritization.
+      options:
+        - "severity: low"
+        - "severity: medium"
+        - "severity: high"
+        - "severity: critical"
+    validations:
+      required: true
+  - type: dropdown
+    id: reproducibility
+    attributes:
+      label: Reproducibility
+      description: Triage metadata used for prioritization.
+      options:
+        - "reproducible: always"
+        - "reproducible: sometimes"
+        - "reproducible: unable"
+    validations:
+      required: true
+  - type: dropdown
+    id: regression
+    attributes:
+      label: Regression
+      description: Triage metadata used for prioritization.
+      options:
+        - "regression: yes"
+        - "regression: no"
+        - "regression: unknown"
+    validations:
+      required: true
+  - type: dropdown
+    id: affects-latest
+    attributes:
+      label: Affects latest release
+      description: Triage metadata used for prioritization.
+      options:
+        - "latest-release: yes"
+        - "latest-release: no"
+        - "latest-release: unknown"
+    validations:
+      required: true
   - type: input
     id: user-story
     attributes:
@@ -16,6 +61,14 @@ body:
       placeholder: As a [user description], I want [desired action] so that [desired outcome].
     validations:
       required: true
+  - type: input
+    id: script-name
+    attributes:
+      label: Script name
+      description: LinuxGSM script name in use.
+      placeholder: vhserver
+    validations:
+      required: true
   - type: input
     id: game
     attributes:
@@ -73,6 +126,22 @@ body:
         - "command: send"
     validations:
       required: true
+  - type: textarea
+    id: expected-behavior
+    attributes:
+      label: Expected behavior
+      description: What should happen?
+      placeholder: Describe the expected result.
+    validations:
+      required: true
+  - type: textarea
+    id: actual-behavior
+    attributes:
+      label: Actual behavior
+      description: What actually happens?
+      placeholder: Describe the observed result.
+    validations:
+      required: true
   - type: textarea
     id: further-info
     attributes:
@@ -81,11 +150,19 @@ body:
       placeholder: Tell us what you see!
     validations:
       required: true
+  - type: checkboxes
+    id: prechecks
+    attributes:
+      label: Pre-checks
+      description: Confirm standard troubleshooting has been completed.
+      options:
+        - label: I ran update and validate before reporting this issue.
+          required: true
   - type: textarea
     id: logs
     attributes:
       label: Relevant log output
-      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
+      description: Include the exact command used and the full related output (debug/details if available). This will be automatically formatted into code.
       render: shell
   - type: textarea
     id: steps
@@ -97,3 +174,5 @@ body:
         2. Click on '....'
         3. Scroll down to '....'
         4. See error
+    validations:
+      required: true

+ 3 - 0
.github/ISSUE_TEMPLATE/config.yml

@@ -6,3 +6,6 @@ contact_links:
   - name: Discord Server
     about: Join the LinuxGSM Discord community server. Discuss your LinuxGSM setup, get help and advice
     url: https://linuxgsm.com/discord
+  - name: Report a security vulnerability
+    about: Please report security vulnerabilities privately, not in public issues.
+    url: https://github.com/GameServerManagers/LinuxGSM/security/advisories/new

+ 41 - 1
.github/ISSUE_TEMPLATE/feature_request.yml

@@ -8,6 +8,17 @@ body:
     attributes:
       value: |
         Thanks for taking the time to fill out this feature request!
+  - type: dropdown
+    id: priority
+    attributes:
+      label: Priority
+      description: How important is this feature to you?
+      options:
+        - "priority: low"
+        - "priority: medium"
+        - "priority: high"
+    validations:
+      required: true
   - type: input
     id: user-story
     attributes:
@@ -71,12 +82,41 @@ body:
         - "command: update-lgsm"
         - "command: wipe"
         - "command: send"
+    validations:
+      required: false
+  - type: textarea
+    id: problem-statement
+    attributes:
+      label: Problem statement
+      description: What is painful today, and why is this needed?
+      placeholder: Describe the current limitation or pain point.
+    validations:
+      required: true
+  - type: dropdown
+    id: scope-impact
+    attributes:
+      label: Scope and impact
+      description: Which area would this change impact?
+      options:
+        - "scope: single game"
+        - "scope: multiple games"
+        - "scope: all servers"
+        - "scope: documentation only"
+        - "scope: ci/cd or automation"
+        - "scope: other"
     validations:
       required: true
   - type: textarea
     id: further-info
     attributes:
       label: Further information
-      description: A clear description of what the feature is and any ideas on how to achieve this.
+      description: A clear description of the proposed solution and any implementation ideas.
     validations:
       required: true
+  - type: textarea
+    id: alternatives
+    attributes:
+      label: Alternatives considered
+      description: Describe alternatives or workarounds you considered.
+    validations:
+      required: false

+ 33 - 5
.github/ISSUE_TEMPLATE/server_request.yml

@@ -7,7 +7,7 @@ body:
   - type: markdown
     attributes:
       value: |
-        Thanks for taking the time to fill out this game server!
+        Thanks for taking the time to fill out this game server request!
   - type: input
     id: game-server
     attributes:
@@ -15,11 +15,19 @@ body:
       description: What game server would you like to add?
     validations:
       required: true
+  - type: checkboxes
+    id: dedicated-server
+    attributes:
+      label: Dedicated server
+      description: Confirm this is a dedicated server request and not client hosting.
+      options:
+        - label: "Yes, this is a dedicated server (not client hosting)."
+          required: true
   - type: checkboxes
     id: on-linux
     attributes:
       label: Linux support
-      description: Does this game server have Linux support? (not wine)
+      description: Does this game server have native Linux server support? (not wine)
       options:
         - label: "Yes"
     validations:
@@ -38,20 +46,40 @@ body:
     id: steam-id
     attributes:
       label: Steam appid
-      description: What is the Steam appid of the game server? Use SteamDB to get the appid. (https://steamdb.info).
+      description: What is the Steam appid of the dedicated server? Required when Steam is Yes. Use SteamDB to get the appid (https://steamdb.info).
       placeholder: "892970"
     validations:
       required: false
+  - type: textarea
+    id: official-docs
+    attributes:
+      label: Official dedicated server documentation
+      description: Provide official documentation links for installing/running the dedicated server.
+      placeholder: |
+        https://example.com/docs/server-setup
+        https://example.com/docs/dedicated-server
+    validations:
+      required: true
+  - type: textarea
+    id: linux-binary-proof
+    attributes:
+      label: Linux binary proof
+      description: Provide evidence that Linux server binaries are available (official docs/download links/version notes).
+      placeholder: |
+        https://example.com/downloads/linux-dedicated-server
+        https://example.com/release-notes/linux-server
+    validations:
+      required: true
   - type: textarea
     id: guides
     attributes:
       label: Guides
-      description: Links to guides on how to install the game server
+      description: Links to community or third-party guides on how to install the game server.
   - type: checkboxes
     id: terms
     attributes:
       label: Code of Conduct
-      description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com)
+      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/GameServerManagers/LinuxGSM/blob/master/CODE_OF_CONDUCT.md)
       options:
         - label: I agree to follow this project's Code of Conduct
           required: true

+ 29 - 0
.github/instructions/pr-review.instructions.md

@@ -0,0 +1,29 @@
+---
+title: "LinuxGSM PR Review Guidance"
+applyTo: "**"
+description: "Use when reviewing pull requests in LinuxGSM; prioritize regressions, behavior changes, shell safety, and missing tests over style-only feedback."
+---
+
+Focus review effort on correctness and operational safety first.
+
+Primary priorities:
+
+- Identify behavior regressions and compatibility risks.
+- Flag unsafe shell patterns (`rm -rf`, unquoted vars, unchecked command failures).
+- Verify workflow changes do not weaken permissions or secret handling.
+- Check for missing tests/validation when logic changes.
+- Confirm labels, templates, and automation rules stay internally consistent.
+
+Feedback expectations:
+
+- Give concrete, actionable findings with file and reason.
+- Prefer high-signal issues over style nits.
+- If no defects are found, state that clearly and mention residual risk areas.
+- Suggest minimal, low-risk fixes before proposing broad refactors.
+
+LinuxGSM-specific checks:
+
+- Shell scripts should preserve robust defaults (`set -euo pipefail` where appropriate).
+- Label/workflow updates should avoid duplicate or stale taxonomy.
+- Automation should fail safe (log and continue for advisory AI; block on true CI errors).
+- Keep issue/PR automation rules aligned with templates and existing labels.

+ 73 - 13
.github/labeler.yml

@@ -69,7 +69,7 @@
 "game: Ballistic Overkill":
   - "/(Ballistic Overkill)/i"
 "game: BATTALION: Legacy":
-  - "/(BATTALION: Legacy)/i"
+  - "/(BATTALION: Legacy|Battalion 1944)/i"
 "game: Barotrauma":
   - "/(Barotrauma)/i"
 "game: Counter-Strike: Global Offensive":
@@ -77,21 +77,27 @@
 "game: Counter-Strike 2":
   - "/(Counter-Strike 2|CS2)/i"
 "game: Counter-Strike: Source":
-  - "/(Counter-Strike: Source|CS:S)/i"
+  - "/(Counter-Strike: Source|Counter Strike: Source|CS:S)/i"
 "game: Counter-Strike 1.6":
   - "/(Counter-Strike 1.6|Counter Strike 1.6|CS 1.6|cs1.6)/i"
-"game: Dayz":
-  - "/(Dayz)/i"
+"game: DayZ":
+  - "/(DayZ|Dayz)/i"
+"game: Deathmatch Classic":
+  - "/(Deathmatch Classic|Death Match Classic|DMC)/i"
 "game: Don't Starve Together":
   - "/(Don't Starve Together|Dont Starve Together|DST)/i"
 "game: Eco":
   - "/(^Eco$)/i"
 "game: Factorio":
   - "/(Factorio)/i"
-"game: Garry's Mod":
+"game: Garrys Mod":
   - "/(Garry's Mod|Garrys Mod|GMod)/i"
+"game: Hurtworld":
+  - "/(Hurtworld|Hurtword)/i"
+"game: Insurgency":
+  - "/(^Insurgency$|Insurgecy)/i"
 "game: Insurgency: Sandstorm":
-  - "/(Insurgency: Sandstorm|Insurgency)/i"
+  - "/(Insurgency: Sandstorm)/i"
 "game: Killing Floor 2":
   - "/(Killing Floor 2|KF2)/i"
 "game: Left 4 Dead 2":
@@ -100,18 +106,20 @@
   - "/(Minecraft)((?!bedrock).)*$/i"
 "game: Minecraft Bedrock":
   - "/(Bedrock)/i"
-"game: Mumble":
-  - "/(Mumble)/i"
 "game: Project Zomboid":
   - "/(Project Zomboid|PZ)/i"
-"game: Quake 3":
-  - "/(Quake 3|Q3A|q3)/i"
+"game: Quake 3: Arena":
+  - "/(Quake 3: Arena|Quake 3|Q3A|q3)/i"
+"game: Quake World":
+  - "/(Quake World|QuakeWorld)/i"
 "game: Rising World":
   - "/(Rising World)/i"
 "game: Satisfactory":
   - "/(Satisfactory)/i"
 "game: Squad":
   - "/(Squad)/i"
+"game: Squad 44":
+  - "/(Squad 44|Post Scriptum)/i"
 "game: Starbound":
   - "/(Starbound)/i"
 "game: Stationeers":
@@ -120,6 +128,8 @@
   - "/(Teamspeak 3|ts3)/i"
 "game: Rust":
   - "/(Rust)/i"
+"game: Soldier Of Fortune 2: Gold Edition":
+  - "/(Soldier Of Fortune 2: Gold Edition|Soldier of Fortune 2)/i"
 "game: Unturned":
   - "/(Unturned)/i"
 "game: Unreal Tournament 99":
@@ -130,6 +140,8 @@
   - "/(Unreal Tournament 3|ut3)/i"
 "game: Valheim":
   - "/(Valheim)/i"
+"game: Zombie Master: Reborn":
+  - "/(Zombie Master: Reborn|Zombie Master Reborn)/i"
 
 # Info
 "info: alerts":
@@ -157,6 +169,54 @@
 "type: game server request":
   - "/(Server Request)/i"
 "type: bug":
-  - "/(bug)/i"
-"type: feature request":
-  - "/(feature)/i"
+  - "/(\\[bug\\]|bug report|type: bug)/i"
+"type: bugfix":
+  - "/(^fix(\\(.+\\))?:|\\[x\\] Bug fix)/im"
+"type: feature":
+  - "/(feature request|new feature|^feat(\\(.+\\))?:|\\[x\\] New feature)/im"
+"type: docs":
+  - "/(^docs(\\(.+\\))?:|documentation|\\[x\\] Comment update)/im"
+"type: refactor":
+  - "/(^refactor(\\(.+\\))?:|\\[x\\] Refactor)/im"
+"type: chore":
+  - "/(^chore(\\(.+\\))?:|^ci(\\(.+\\))?:)/im"
+
+# Severity (bug reports)
+"severity: low":
+  - "/(severity: low)/i"
+"severity: medium":
+  - "/(severity: medium)/i"
+"severity: high":
+  - "/(severity: high)/i"
+"severity: critical":
+  - "/(severity: critical)/i"
+
+# Reproducibility (bug reports)
+"reproducible: always":
+  - "/(reproducible: always)/i"
+"reproducible: sometimes":
+  - "/(reproducible: sometimes)/i"
+"reproducible: unable":
+  - "/(reproducible: unable)/i"
+
+# Regression (bug reports)
+"regression: yes":
+  - "/(regression: yes)/i"
+
+# Priority (feature requests)
+"priority: low":
+  - "/(priority: low)/i"
+"priority: medium":
+  - "/(priority: medium)/i"
+"priority: high":
+  - "/(priority: high)/i"
+
+# Scope (feature requests)
+"scope: single game":
+  - "/(scope: single game)/i"
+"scope: multiple games":
+  - "/(scope: multiple games)/i"
+"scope: all servers":
+  - "/(scope: all servers)/i"
+"scope: documentation":
+  - "/(scope: documentation)/i"

+ 25 - 3
.github/pull_request_template.md

@@ -12,18 +12,40 @@ Fixes #[issue]
 - [ ] Refactor (restructures existing code).
 - [ ] Comment update (typo, spelling, explanation, examples, etc).
 
+## Testing
+
+Please list the exact validation you performed and the outcome.
+
+- Commands/tests run:
+- Result:
+- Environment used (distro/version):
+
+## Risk and rollback
+
+- Risk level: low / medium / high
+- Rollback plan:
+
+## Breaking changes
+
+- [ ] No breaking changes.
+- [ ] Breaking changes included (describe below).
+
+## Documentation impact
+
+- [ ] No documentation update required.
+- [ ] User documentation update required.
+- [ ] Developer documentation update required.
+
 ## Checklist
 
 PR will not be merged until all steps are complete.
 
 - [ ] This pull request links to an issue.
-- [ ] This pull request uses the `develop` branch as its base.
+- [ ] This pull request uses the develop branch as its base.
 - [ ] This pull request subject follows the Conventional Commits standard.
 - [ ] This code follows the style guidelines of this project.
 - [ ] I have performed a self-review of my code.
-- [ ] I have checked that this code is commented where required.
 - [ ] I have provided a detailed enough description of this PR.
-- [ ] I have checked if documentation needs updating.
 
 ## Documentation
 

+ 87 - 0
.github/scripts/sync-game-labels.sh

@@ -0,0 +1,87 @@
+#!/usr/bin/env bash
+# sync-game-labels.sh
+# Reads lgsm/data/serverlist.csv and ensures a "game: <name>" label exists in
+# the GitHub repo for every unique game name. Safe to run multiple times.
+#
+# Requires: gh CLI authenticated with issues:write scope.
+# Usage:    .github/scripts/sync-game-labels.sh [OWNER/REPO]
+#
+# The OWNER/REPO argument is optional; if omitted gh uses the current repo.
+
+set -euo pipefail
+
+REPO="${1:-}"
+SERVERLIST="lgsm/data/serverlist.csv"
+LABEL_COLOR="5b21b6"
+LABEL_PREFIX="game: "
+
+normalize_label() {
+	printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
+}
+
+if [[ ! -f "${SERVERLIST}" ]]; then
+  echo "ERROR: ${SERVERLIST} not found. Run from the repository root."
+  exit 1
+fi
+
+declare -A EXISTING_COLORS=()
+declare -A EXISTING_DESCRIPTIONS=()
+declare -A EXISTING_NAMES=()
+
+# Fetch all existing game label metadata once (up to 1000) and cache locally.
+echo "Fetching existing labels..."
+while IFS=$'\t' read -r NAME COLOR DESCRIPTION; do
+  [[ -n "${NAME}" ]] || continue
+  EXISTING_COLORS["${NAME}"]="${COLOR}"
+  EXISTING_DESCRIPTIONS["${NAME}"]="${DESCRIPTION}"
+  EXISTING_NAMES["$(normalize_label "${NAME}")"]="${NAME}"
+done < <(
+  gh label list --limit 1000 --json name,color,description ${REPO:+--repo "$REPO"} \
+    | jq -r '.[] | select(.name | startswith("game: ")) | [.name, .color, (.description // "")] | @tsv'
+)
+
+# Parse unique game names from the CSV (column 3, skip header).
+mapfile -t GAMES < <(
+  tail -n +2 "${SERVERLIST}" \
+    | cut -d',' -f3 \
+    | sort -u
+)
+
+CREATED=0
+UPDATED=0
+UNCHANGED=0
+
+for GAME in "${GAMES[@]}"; do
+  LABEL="${LABEL_PREFIX}${GAME}"
+  DESCRIPTION="Issues related to ${GAME}"
+  NORMALIZED_LABEL="$(normalize_label "${LABEL}")"
+
+  if [[ -v EXISTING_NAMES["${NORMALIZED_LABEL}"] ]]; then
+    CURRENT_LABEL="${EXISTING_NAMES["${NORMALIZED_LABEL}"]}"
+    CURRENT_COLOR="${EXISTING_COLORS["${CURRENT_LABEL}"]}"
+    CURRENT_DESCRIPTION="${EXISTING_DESCRIPTIONS["${CURRENT_LABEL}"]}"
+
+    if [[ "${CURRENT_LABEL}" != "${LABEL}" || "${CURRENT_COLOR}" != "${LABEL_COLOR}" || "${CURRENT_DESCRIPTION}" != "${DESCRIPTION}" ]]; then
+      echo "  update  ${LABEL}"
+      gh label edit "${CURRENT_LABEL}" \
+        --name "${LABEL}" \
+        --color "${LABEL_COLOR}" \
+        --description "${DESCRIPTION}" \
+        ${REPO:+--repo "$REPO"}
+      (( UPDATED++ )) || true
+    else
+      echo "  ok      ${LABEL}"
+      (( UNCHANGED++ )) || true
+    fi
+  else
+    echo "  create  ${LABEL}"
+    gh label create "${LABEL}" \
+      --color "${LABEL_COLOR}" \
+      --description "${DESCRIPTION}" \
+      ${REPO:+--repo "$REPO"}
+    (( CREATED++ )) || true
+  fi
+done
+
+echo ""
+echo "Done. Created: ${CREATED}  Updated: ${UPDATED}  Unchanged: ${UNCHANGED}"

+ 243 - 0
.github/workflows/ai-triage.yml

@@ -0,0 +1,243 @@
+name: AI Issue Triage
+on:
+  issues:
+    types:
+      - opened
+      - edited
+
+# Note: This workflow uses GitHub Models which may not be available
+# in all environments. If GitHub Models API returns 401 or is unavailable,
+# the workflow will skip gracefully and the issue will still be processed
+# by other automation (labeler, etc.)
+
+permissions:
+  issues: write
+  contents: read
+  models: read
+
+jobs:
+  ai-triage:
+    if: github.repository_owner == 'GameServerManagers'
+    runs-on: ubuntu-latest
+    steps:
+      - name: Triage issue with GitHub Models
+        uses: actions/github-script@v7
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          script: |
+            const title  = context.payload.issue.title  || '';
+            const body   = context.payload.issue.body   || '';
+            const number = context.payload.issue.number;
+            const owner  = context.repo.owner;
+            const repo   = context.repo.repo;
+            const AI_MARKER = '<!-- ai-triage -->';
+
+            function parseTriageResponse(raw) {
+              const input = (raw || '').trim();
+              if (!input) return {};
+
+              const candidates = [input];
+              const fenced = input.match(/```(?:json)?\s*([\s\S]*?)```/i);
+              if (fenced?.[1]) candidates.push(fenced[1].trim());
+
+              const firstBrace = input.indexOf('{');
+              const lastBrace = input.lastIndexOf('}');
+              if (firstBrace !== -1 && lastBrace > firstBrace) {
+                candidates.push(input.slice(firstBrace, lastBrace + 1));
+              }
+
+              for (const candidate of candidates) {
+                try {
+                  return JSON.parse(candidate);
+                } catch (_err) {
+                  // Continue trying fallbacks.
+                }
+              }
+
+              return {};
+            }
+
+            // For short bodies, apply "needs: more info" label directly.
+            // Skip the AI call but still label the issue.
+            const isShortBody = body.trim().length < 80;
+            if (isShortBody) {
+              try {
+                await github.rest.issues.addLabels({
+                  owner, repo, issue_number: number,
+                  labels: ['needs: more info'],
+                });
+              } catch (err) {
+                console.log('Could not apply label for short body:', err.message);
+              }
+              return;
+            }
+
+            // ── Call GitHub Models ────────────────────────────────────────
+            // Note: GitHub Models access may not be available in all environments.
+            // If the API returns 401 (Unauthorized), it means GitHub Models is not
+            // enabled for this repository or the current token lacks access.
+            // The workflow will gracefully skip AI triage and continue.
+            let triage;
+            try {
+              const res = await fetch(
+                `https://models.github.ai/orgs/${owner}/inference/chat/completions`,
+                {
+                  method: 'POST',
+                  headers: {
+                    'Accept': 'application/vnd.github+json',
+                    'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`,
+                    'X-GitHub-Api-Version': '2026-03-10',
+                    'Content-Type': 'application/json',
+                  },
+                  body: JSON.stringify({
+                    model: 'openai/gpt-4.1-mini',
+                    temperature: 0.1,
+                    max_tokens: 400,
+                    messages: [
+                      {
+                        role: 'system',
+                        content:
+                          'You are a triage assistant for LinuxGSM, an open-source ' +
+                          'Linux game server manager. Your role is to:\n' +
+                          '1. Analyze issue quality (completeness, clarity)\n' +
+                          '2. Extract game names mentioned in the issue, even if misspelled or abbreviated\n' +
+                          '3. Suggest corrections for likely typos using fuzzy matching\n' +
+                          '4. Respond ONLY with a valid JSON object — no markdown fences.\n\n' +
+                          'Common game name variations and typos you should recognize:\n' +
+                          '- "Valhiem" → "Valheim"\n' +
+                          '- "Rrust" → "Rust"\n' +
+                          '- "Conterstrike" / "CS" / "CSGO" → "Counter-Strike: Global Offensive"\n' +
+                          '- "Garrys" / "GMod" → "Garrys Mod"\n' +
+                          '- "ARK" / "Ark" → "ARK: Survival Evolved"\n' +
+                          '- "DayZ" / "Dayz" → "DayZ"\n' +
+                          '- "Insurgency Sandstorm" / "Insurgency 2" → "Insurgency: Sandstorm"',
+                      },
+                      {
+                        role: 'user',
+                        content:
+                          `Title: ${title}\n\nBody:\n${body.slice(0, 3000)}\n\n` +
+                          'Respond with this JSON schema:\n' +
+                          '{\n' +
+                          '  "quality": "good" | "ok" | "poor",\n' +
+                          '  "missing_info": ["list of specific missing fields"],\n' +
+                          '  "detected_game": "canonical game name if one is mentioned, or null",\n' +
+                          '  "game_confidence": "high" | "medium" | "low" | null,\n' +
+                          '  "game_note": "correction suggestion if the user misspelled a game name, or empty string",\n' +
+                          '  "comment": "one or two sentence note to the reporter, or empty string"\n' +
+                          '}',
+                      },
+                    ],
+                  }),
+                }
+              );
+
+              // GitHub Models may return 401 if not available for this repository.
+              // This is expected and not an error — the workflow simply skips AI triage.
+              if (!res.ok) {
+                console.log(`GitHub Models returned ${res.status} — skipping AI triage.`);
+                return;
+              }
+
+              const data = await res.json();
+              const raw  = data.choices?.[0]?.message?.content || '{}';
+              triage = parseTriageResponse(raw);
+            } catch (err) {
+              // Never fail the workflow if the AI call errors — it's advisory only.
+              console.log('AI triage skipped:', err.message);
+              return;
+            }
+
+            if (!triage || typeof triage !== 'object') {
+              triage = {};
+            }
+
+            // ── Act on the result ────────────────────────────────────────
+            const isPoor    = triage.quality === 'poor';
+            const missing   = Array.isArray(triage.missing_info) ? triage.missing_info : [];
+            const hasIssues = isPoor || missing.length > 0;
+
+            // Prepare labels to apply
+            const labelsToApply = [];
+
+            // Check if a game was detected with high confidence
+            const detectedGame = triage.detected_game;
+            const gameConfidence = triage.game_confidence;
+
+            if (detectedGame && gameConfidence === 'high') {
+              labelsToApply.push(`game: ${detectedGame}`);
+            }
+
+            // Apply "needs: more info" label if quality issues detected
+            if (hasIssues) {
+              labelsToApply.push('needs: more info');
+            }
+
+            // Apply labels one-by-one so a single failure does not block all labels.
+            const uniqueLabels = [...new Set(labelsToApply)];
+            for (const label of uniqueLabels) {
+              try {
+                await github.rest.issues.addLabels({
+                  owner,
+                  repo,
+                  issue_number: number,
+                  labels: [label],
+                });
+              } catch (err) {
+                console.log(`Could not apply label "${label}":`, err.message);
+              }
+            }
+
+            // Post a comment only when there is something specific to say
+            const gameNote = triage.game_note || '';
+            const reporterComment = triage.comment || '';
+
+            if (!hasIssues && !gameNote) return;
+
+            const missingBlock = missing.length > 0
+              ? `\n\n**Missing information:**\n${missing.map(m => `- ${m}`).join('\n')}`
+              : '';
+
+            const gameBlock = gameNote
+              ? `\n\n**Game name note:** ${gameNote}`
+              : '';
+
+            const triageCommentBody =
+              `${AI_MARKER}\n` +
+              `Thanks for opening this issue! 👋\n\n` +
+              `${reporterComment}` +
+              `${missingBlock}` +
+              `${gameBlock}\n\n` +
+              `_This note was generated automatically by AI triage and may not be perfect. ` +
+              `A maintainer will review shortly._`;
+
+            try {
+              const comments = await github.rest.issues.listComments({
+                owner,
+                repo,
+                issue_number: number,
+                per_page: 100,
+              });
+
+              const existingAiComment = comments.data.find(
+                (comment) => comment.user?.type === 'Bot' && comment.body?.includes(AI_MARKER)
+              );
+
+              if (existingAiComment) {
+                await github.rest.issues.updateComment({
+                  owner,
+                  repo,
+                  comment_id: existingAiComment.id,
+                  body: triageCommentBody,
+                });
+              } else {
+                await github.rest.issues.createComment({
+                  owner,
+                  repo,
+                  issue_number: number,
+                  body: triageCommentBody,
+                });
+              }
+            } catch (err) {
+              console.log('Could not post comment:', err.message);
+            }

+ 28 - 2
.github/workflows/labeler.yml

@@ -4,14 +4,24 @@ on:
     types:
       - opened
       - edited
+  pull_request:
+    types:
+      - opened
+      - edited
+      - synchronize
+      - reopened
 
 permissions:
   issues: write
+  pull-requests: write
   contents: read
 
+env:
+  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
+
 jobs:
   issue-labeler:
-    if: github.repository_owner == 'GameServerManagers'
+    if: github.repository_owner == 'GameServerManagers' && github.event_name == 'issues'
     runs-on: ubuntu-latest
     steps:
       - name: Issue Labeler
@@ -21,12 +31,28 @@ jobs:
           configuration-path: .github/labeler.yml
           enable-versioned-regex: 0
           include-title: 1
+          sync-labels: 1
+
+  pr-labeler:
+    if: github.repository_owner == 'GameServerManagers' && github.event_name == 'pull_request'
+    runs-on: ubuntu-latest
+    steps:
+      - name: PR Labeler
+        uses: github/issue-labeler@v3.4
+        with:
+          repo-token: "${{ secrets.GITHUB_TOKEN }}"
+          configuration-path: .github/labeler.yml
+          enable-versioned-regex: 0
+          include-title: 1
+          include-body: 0
+          sync-labels: 1
 
   is-sponsor-label:
-    if: github.repository_owner == 'GameServerManagers'
+    if: github.repository_owner == 'GameServerManagers' && github.event_name == 'issues'
     runs-on: ubuntu-latest
     steps:
       - name: Is Sponsor Label
+        if: github.event.action == 'opened'
         uses: JasonEtco/is-sponsor-label-action@v2
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

+ 28 - 0
.github/workflows/sync-game-labels.yml

@@ -0,0 +1,28 @@
+name: Sync Game Labels
+on:
+  push:
+    branches:
+      - master
+      - develop
+    paths:
+      - "lgsm/data/serverlist.csv"
+  workflow_dispatch: {}
+
+permissions:
+  issues: write
+  contents: read
+
+jobs:
+  sync-game-labels:
+    if: github.repository_owner == 'GameServerManagers'
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v5
+
+      - name: Sync game labels from serverlist
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          chmod +x .github/scripts/sync-game-labels.sh
+          .github/scripts/sync-game-labels.sh

+ 145 - 118
CODE_OF_CONDUCT.md

@@ -2,131 +2,158 @@
 
 ## Our Pledge
 
-We as members, contributors, and leaders pledge to make participation in our
-community a harassment-free experience for everyone, regardless of age, body
-size, visible or invisible disability, ethnicity, sex characteristics, gender
-identity and expression, level of experience, education, socio-economic status,
-nationality, personal appearance, race, caste, color, religion, or sexual identity
-and orientation.
-
-We pledge to act and interact in ways that contribute to an open, welcoming,
-diverse, inclusive, and healthy community.
-
-## Our Standards
-
-Examples of behavior that contributes to a positive environment for our
-community include:
-
-- Demonstrating empathy and kindness toward other people
-- Being respectful of differing opinions, viewpoints, and experiences
-- Giving and gracefully accepting constructive feedback
-- Accepting responsibility and apologizing to those affected by our mistakes,
-  and learning from the experience
-- Focusing on what is best not just for us as individuals, but for the
-  overall community
-
-Examples of unacceptable behavior include:
-
-- The use of sexualized language or imagery, and sexual attention or
-  advances of any kind
-- Trolling, insulting or derogatory comments, and personal or political attacks
-- Public or private harassment
-- Publishing others' private information, such as a physical or email
-  address, without their explicit permission
-- Other conduct which could reasonably be considered inappropriate in a
-  professional setting
-
-## Enforcement Responsibilities
-
-Community leaders are responsible for clarifying and enforcing our standards of
-acceptable behavior and will take appropriate and fair corrective action in
-response to any behavior that they deem inappropriate, threatening, offensive,
-or harmful.
-
-Community leaders have the right and responsibility to remove, edit, or reject
-comments, commits, code, wiki edits, issues, and other contributions that are
-not aligned to this Code of Conduct, and will communicate reasons for moderation
-decisions when appropriate.
+We pledge to make our community welcoming, safe, and equitable for all.
+
+We are committed to fostering an environment that respects and promotes the
+dignity, rights, and contributions of all individuals, regardless of characteristics
+including race, ethnicity, caste, color, age, physical characteristics,
+neurodiversity, disability, sex or gender, gender identity or expression, sexual
+orientation, language, philosophy or religion, national or social origin,
+socio-economic position, level of education, or other status. The same privileges of
+participation are extended to everyone who participates in good faith and in
+accordance with this Covenant.
+
+## Encouraged Behaviors
+
+While acknowledging differences in social norms, we all strive to meet our
+community's expectations for positive behavior. We also understand that our words
+and actions may be interpreted differently than we intend based on culture,
+background, or native language.
+
+With these considerations in mind, we agree to behave mindfully toward each other
+and act in ways that center our shared values, including:
+
+1. Respecting the **purpose of our community**, our activities, and our ways of gathering.
+2. Engaging **kindly and honestly** with others.
+3. Respecting **different viewpoints** and experiences.
+4. **Taking responsibility** for our actions and contributions.
+5. Gracefully giving and accepting **constructive feedback**.
+6. Committing to **repairing harm** when it occurs.
+7. Behaving in other ways that promote and sustain the **well-being of our community**.
+
+## Restricted Behaviors
+
+We agree to restrict the following behaviors in our community. Instances,
+threats, and promotion of these behaviors are violations of this Code of Conduct.
+
+1. **Harassment.** Violating explicitly expressed boundaries or engaging in
+   unnecessary personal attention after any clear request to stop.
+2. **Character attacks.** Making insulting, demeaning, or pejorative comments
+   directed at a community member or group of people.
+3. **Stereotyping or discrimination.** Characterizing anyone's personality or
+   behavior on the basis of immutable identities or traits.
+4. **Sexualization.** Behaving in a way that would generally be considered
+   inappropriately intimate in the context or purpose of the community.
+5. **Violating confidentiality.** Sharing or acting on someone's personal or
+   private information without their permission.
+6. **Endangerment.** Causing, encouraging, or threatening violence or other harm
+   toward any person or group.
+7. Behaving in other ways that **threaten the well-being** of our community.
+
+### Other Restrictions
+
+1. **Misleading identity.** Impersonating someone else for any reason, or
+   pretending to be someone else to evade enforcement actions.
+2. **Failing to credit sources.** Not properly crediting the sources of content
+   you contribute.
+3. **Promotional materials.** Sharing marketing or other commercial content in a
+   way that is outside the norms of the community.
+4. **Irresponsible communication.** Failing to responsibly present content which
+   includes, links or describes any other restricted behaviors.
+
+## Reporting an Issue
+
+Tensions can occur between community members even when they are trying their best
+to collaborate. Not every conflict represents a code of conduct violation, and this
+Code of Conduct reinforces encouraged behaviors and norms that can help avoid
+conflicts and minimize harm.
+
+When an incident does occur, it is important to report it promptly. To report a
+possible violation, please use one of the following methods:
+
+- **GitHub (private):** [Submit a private security advisory](https://github.com/GameServerManagers/LinuxGSM/security/advisories/new)
+- **Discord:** Contact a moderator via the [LinuxGSM Discord server](https://linuxgsm.com/discord)
+
+Community Moderators take reports of violations seriously and will make every
+effort to respond in a timely manner. They will investigate all reports of code of
+conduct violations, reviewing messages, logs, and recordings, or interviewing
+witnesses and other participants. Community Moderators will keep investigation and
+enforcement actions as transparent as possible while prioritizing safety and
+confidentiality. In order to honor these values, enforcement actions are carried out
+in private with the involved parties, but communicating to the whole community may
+be part of a mutually agreed upon resolution.
+
+## Addressing and Repairing Harm
+
+If an investigation by the Community Moderators finds that this Code of Conduct
+has been violated, the following enforcement ladder may be used to determine how
+best to repair harm, based on the incident's impact on the individuals involved
+and the community as a whole. Depending on the severity of a violation, lower
+rungs on the ladder may be skipped.
+
+1. **Warning**
+   1. Event: A violation involving a single incident or series of incidents.
+   2. Consequence: A private, written warning from the Community Moderators.
+   3. Repair: Examples of repair include a private written apology, acknowledgement
+      of responsibility, and seeking clarification on expectations.
+
+2. **Temporarily Limited Activities**
+   1. Event: A repeated incidence of a violation that previously resulted in a
+      warning, or the first incidence of a more serious violation.
+   2. Consequence: A private, written warning with a time-limited cooldown period
+      designed to underscore the seriousness of the situation and give the community
+      members involved time to process the incident. The cooldown period may be
+      limited to particular communication channels or interactions with particular
+      community members.
+   3. Repair: Examples of repair may include making an apology, using the cooldown
+      period to reflect on actions and impact, and being thoughtful about
+      re-entering community spaces after the period is over.
+
+3. **Temporary Suspension**
+   1. Event: A pattern of repeated violation which the Community Moderators have
+      tried to address with warnings, or a single serious violation.
+   2. Consequence: A private written warning with conditions for return from
+      suspension. In general, temporary suspensions give the person being suspended
+      time to reflect upon their behavior and possible corrective actions.
+   3. Repair: Examples of repair include respecting the spirit of the suspension,
+      meeting the specified conditions for return, and being thoughtful about how to
+      reintegrate with the community when the suspension is lifted.
+
+4. **Permanent Ban**
+   1. Event: A pattern of repeated code of conduct violations that other steps on
+      the ladder have failed to resolve, or a violation so serious that the Community
+      Moderators determine there is no way to keep the community safe with this
+      person as a member.
+   2. Consequence: Access to all community spaces, tools, and communication channels
+      is removed. In general, permanent bans should be rarely used, should have
+      strong reasoning behind them, and should only be resorted to if working through
+      other remedies has failed to change the behavior.
+   3. Repair: There is no possible repair in cases of this severity.
+
+This enforcement ladder is intended as a guideline. It does not limit the ability
+of Community Moderators to use their discretion and judgment, in keeping with the
+best interests of our community.
 
 ## Scope
 
-This Code of Conduct applies within all community spaces, and also applies when
-an individual is officially representing the community in public spaces.
-Examples of representing our community include using an official e-mail address,
+This Code of Conduct applies within all community spaces, and also applies when an
+individual is officially representing the community in public or other spaces.
+Examples of representing our community include using an official email address,
 posting via an official social media account, or acting as an appointed
 representative at an online or offline event.
 
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported to the community leaders responsible for enforcement at
-[INSERT CONTACT METHOD].
-All complaints will be reviewed and investigated promptly and fairly.
-
-All community leaders are obligated to respect the privacy and security of the
-reporter of any incident.
-
-## Enforcement Guidelines
-
-Community leaders will follow these Community Impact Guidelines in determining
-the consequences for any action they deem in violation of this Code of Conduct:
-
-### 1. Correction
-
-**Community Impact**: Use of inappropriate language or other behavior deemed
-unprofessional or unwelcome in the community.
-
-**Consequence**: A private, written warning from community leaders, providing
-clarity around the nature of the violation and an explanation of why the
-behavior was inappropriate. A public apology may be requested.
-
-### 2. Warning
-
-**Community Impact**: A violation through a single incident or series
-of actions.
-
-**Consequence**: A warning with consequences for continued behavior. No
-interaction with the people involved, including unsolicited interaction with
-those enforcing the Code of Conduct, for a specified period of time. This
-includes avoiding interactions in community spaces as well as external channels
-like social media. Violating these terms may lead to a temporary or
-permanent ban.
-
-### 3. Temporary Ban
-
-**Community Impact**: A serious violation of community standards, including
-sustained inappropriate behavior.
-
-**Consequence**: A temporary ban from any sort of interaction or public
-communication with the community for a specified period of time. No public or
-private interaction with the people involved, including unsolicited interaction
-with those enforcing the Code of Conduct, is allowed during this period.
-Violating these terms may lead to a permanent ban.
-
-### 4. Permanent Ban
-
-**Community Impact**: Demonstrating a pattern of violation of community
-standards, including sustained inappropriate behavior, harassment of an
-individual, or aggression toward or disparagement of classes of individuals.
-
-**Consequence**: A permanent ban from any sort of public interaction within
-the community.
-
 ## Attribution
 
-This Code of Conduct is adapted from the [Contributor Covenant][homepage],
-version 2.1, available at
-[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
-
-Community Impact Guidelines were inspired by
-[Mozilla's code of conduct enforcement ladder][mozilla coc].
+This Code of Conduct is adapted from the Contributor Covenant, version 3.0,
+permanently available at <https://www.contributor-covenant.org/version/3/0/>.
 
-For answers to common questions about this code of conduct, see the FAQ at
-[https://www.contributor-covenant.org/faq][faq]. Translations are available
-at [https://www.contributor-covenant.org/translations][translations].
+Contributor Covenant is stewarded by the Organization for Ethical Source and
+licensed under CC BY-SA 4.0. To view a copy of this license, visit
+<https://creativecommons.org/licenses/by-sa/4.0/>.
 
-[homepage]: https://www.contributor-covenant.org
-[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
-[mozilla coc]: https://github.com/mozilla/diversity
-[faq]: https://www.contributor-covenant.org/faq
-[translations]: https://www.contributor-covenant.org/translations
+For answers to common questions about Contributor Covenant, see the FAQ at
+<https://www.contributor-covenant.org/faq>. Translations are provided at
+<https://www.contributor-covenant.org/translations>. Additional enforcement and
+community guideline resources can be found at
+<https://www.contributor-covenant.org/resources>. The enforcement ladder was
+inspired by the work of [Mozilla's code of conduct team](https://github.com/mozilla/inclusion).