Rust
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.
server/
└── oxide/
└── plugins/
└── TopGVote.cs 2 Define the plugin class
Create the main plugin class that hooks into Oxide lifecycle events.
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
{
"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.
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.
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
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`.