Home / Developers / Rust
🔫

Rust

C# uMod / Oxide

Overview

Reward your Rust players for voting on topgservers. This uMod (Oxide) plugin polls the vote-check API and optionally listens for webhooks to grant kits, economics balance, or custom rewards in real time.

Prerequisites

  • A Rust server with Oxide / uMod installed
  • topgservers API key — generate one in My Servers → API
  • Webhook secret (optional, for instant rewards)
  • Outbound HTTPS access from your server

Installation

1 Create the plugin file

Place a single `.cs` file in your `oxide/plugins/` directory. Oxide will compile it automatically on server start.

Directory structure
server/
└── oxide/
    └── plugins/
        └── TopGVote.cs

2 Define the plugin class

Create the main plugin class that hooks into Oxide lifecycle events.

TopGVote.cs (header)
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Core.Libraries;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;

namespace Oxide.Plugins
{
    [Info("TopGVote", "topgservers", "1.0.0")]
    [Description("Reward players for voting on topgservers")]
    class TopGVote : RustPlugin
    {
        private Configuration config;
        private HashSet<ulong> rewardedToday = new HashSet<ulong>();
        private Timer pollTimer;
    }
}

3 Load the plugin

Drop `TopGVote.cs` into `oxide/plugins/`. Oxide auto-compiles it. Use `oxide.reload TopGVote` to reload after edits.

Configuration

oxide/config/TopGVote.json
{
  "ApiKey": "tgs_your_api_key_here",
  "WebhookSecret": "whsec_your_secret_here",
  "PollIntervalSeconds": 60,
  "RewardCommand": "inventory.giveto {steamid} scrap 100",
  "RewardMessage": "Thanks for voting on topgservers! You received 100 scrap."
}

Vote Check

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

TopGVote.cs — vote check
private void CheckVotes()
{
    foreach (var player in BasePlayer.activePlayerList)
    {
        if (rewardedToday.Contains(player.userID)) continue;

        var headers = new Dictionary<string, string>
        {
            { "Authorization", "Bearer " + config.ApiKey }
        };

        var url = "https://topgservers.net/api/v1/vote-check?username="
            + Uri.EscapeDataString(player.displayName);

        webrequest.Enqueue(url, null, (code, body) =>
        {
            if (code != 200 || body == null) return;

            var result = JsonConvert.DeserializeObject<VoteResponse>(body);
            if (result?.voted == true)
            {
                rewardedToday.Add(player.userID);
                GrantReward(player);
            }
        }, this, RequestMethod.GET, headers);
    }
}

class VoteResponse
{
    public bool voted { get; set; }
    public string last_vote_at { get; set; }
}

void OnServerInitialized()
{
    pollTimer = timer.Every(config.PollIntervalSeconds, CheckVotes);
}

Webhook Receiver

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

TopGVote.cs — webhook HMAC verification
using System.Security.Cryptography;
using System.Text;

private bool VerifySignature(string body, string signatureHeader)
{
    var parts = signatureHeader.Split(',');
    string timestamp = "", hash = "";

    foreach (var part in parts)
    {
        if (part.StartsWith("t=")) timestamp = part.Substring(2);
        if (part.StartsWith("v1=")) hash = part.Substring(3);
    }

    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(config.WebhookSecret));
    var payload = Encoding.UTF8.GetBytes(timestamp + "." + body);
    var computed = BitConverter.ToString(hmac.ComputeHash(payload))
        .Replace("-", "").ToLower();

    return computed == hash;
}

Reward Examples

Reward examples
private void GrantReward(BasePlayer player)
{
    // Give scrap
    player.GiveItem(ItemManager.CreateByName("scrap", 100));

    // Economics plugin
    Economics?.Call("Deposit", player.UserIDString, 500.0);

    // Server Rewards plugin
    ServerRewards?.Call("AddPoints", player.userID, 50);

    // Run console command
    var cmd = config.RewardCommand
        .Replace("{steamid}", player.UserIDString)
        .Replace("{player}", player.displayName);
    rust.RunServerCommand(cmd);

    player.ChatMessage(config.RewardMessage);
}

Notes & Tips

Oxide's `webrequest.Enqueue` is non-blocking — it won't stall your server tick. The `rewardedToday` set resets on plugin reload or server restart; for persistence across restarts, save it to a data file using `Interface.Oxide.DataFileSystem`.