这个东西是什么
Tebex 的 Rust 插件方向源码,用来把 Tebex 商店购买结果发到 Rust 服务器,不是独立商城前台。
预览说明
这个项目不是完整商城前台,不能直接像 PHP 店铺那样点购买。这里给你看源码说明、目录用途和关键代码,方便判断它是不是你要的东西。
README
# Tebex Oxide Plugin for Rust and 7 Days To Die ## Description [Tebex](https://tebex.io/) provides a monetization and donation platform for game servers, allowing server owners to manage in-game purchases, subscriptions, and donations with ease. This plugin acts as a bridge between your **Rust** game server and the **Tebex platform**, enabling you to offer a wide range of virtual items, packages, and services to your players. As this is an Oxide plugin, it is also compatible with **7 Days to Die**. ## Commands The following commands are available through the Tebex Rust Plugin: ### Admin Commands - `/tebex.secret <secret>`: Set your server's secret key - `/tebex.sendlink <player> <packageId>`: Send a purchase link for a package to a player. - `/tebex.forcecheck`: Force run any due online and offline commands. - `/tebex.refresh`: Refresh your store's listings. - `/tebex.report`: Prepare a report that can be submitted to our support team. - `/tebex.ban`: Ban a player from using the store. **Players can only be unbanned from the webstore UI.** - `/tebex.lookup`: Display information about a customer. ### User Commands - `/tebex.help`: Display a list of available commands and their descriptions. - `/tebex.info`: Display public store information. - `/tebex.categories`: View all store categories. - `/tebex.packages`: View all store packages. - `/tebex.checkout <packageId>`: Create a checkout link for a package. - `/tebex.stats`: View your own player statistics in the store. ## Installation To install the Tebex Rust Plugin, follow these steps: 1. Download the latest release of this plugin from [Tebex.io](https://docs.tebex.io/plugin/official-plugins), or choose the latest [Release](https://github.com/tebexio/Tebex-Rust/releases) from this repository. 2. Upload the plugin .cs source file to the `oxide/plugins` directory of your game server. 3. Start the game server. If it is already running, it should automatically load the plugin. If not, run `oxide.reload Tebex` to load the plugin. ## Dev Environment Setup If you wish to contribute to the development of the plugin, you can set up your development environment as follows: **Requirements:** - Python 3 - dotnet - [Oxide](https://umod.org/games/rust) **Setup Instructions:** 1. Clone the repository to an empty folder. 2. Download [Oxide](https://umod.org/games/rust) and unzip it. 3. Add the assemblies `Oxide.Core`, `Oxide.CSharp`, `Oxide.MySql`, `Oxide.Rust`, and `Facebunch.UnityEngine`, `Assembly-CSharp` as a minimum to the project. ## Building and Testing Oxide plugins are basic .cs source files - we do not build a .dll or an executable. Instead, we combine our source files together then ensure it can compile and run on a real server. You can configure a test server in `BuildConfig.py` 1. Ensure your development environment is properly set up per the instructions above. 2. Using `BuildConfig.py.example`, make a `BuildConfig.py` and fill the appropriate values. 3. Run `python3 Build.py`. This will merge any source files configured in `BuildConfig.py` together into the final plugin in `Build/Tebex.cs` ### Build Arguments The full build and test suite can be ran sequentially with these additional arguments: - `--DeployTest`: Runs a deployment script, ideally uploads to a test server. - `--TestRemoteReload`: Test connect to see if a remote Rust server can reload the plugin. - `--OpenDevConsole`: Open interactive RCON console on a test rust server. Each run of the build script will always merge and output the final plugin source file. ## Contributions We welcome contributions from the community. Please refer to the `CONTRIBUTING.md` file for more details. By submitting code to us, you agree to the terms set out in the CONTRIBUTING.md file ## Support This repository is only used for bug reports via GitHub Issues. If you have found a bug, please [open an issue](https://github.com/tebexio/Tebex-Rust/issues). If you are a user requiring support for Tebex, please contact us at https://tebex.io/contact
关键源码
using Newtonsoft.Json;
using Tebex.API;
using Tebex.Triage;
namespace Tebex.Adapters
{
public abstract class BaseTebexAdapter
{
public static BaseTebexAdapter Instance => _adapterInstance.Value;
private static readonly Lazy<BaseTebexAdapter> _adapterInstance = new Lazy<BaseTebexAdapter>();
public static TebexConfig PluginConfig { get; set; } = new TebexConfig();
/** For rate limiting command queue based on next_check */
private static DateTime _nextCheckCommandQueue = DateTime.Now;
// Time checks for our plugin timers.
private static DateTime _nextCheckDeleteCommands = DateTime.Now;
private static DateTime _nextCheckJoinQueue = DateTime.Now;
private static DateTime _nextCheckRefresh = DateTime.Now;
private static List<TebexApi.TebexJoinEventInfo> _eventQueue = new List<TebexApi.TebexJoinEventInfo>();
/** For storing successfully executed commands and deleting them from API */
protected static readonly List<TebexApi.Command> ExecutedCommands = new List<TebexApi.Command>();
/** Allow pausing all web requests if rate limits are received from remote */
protected bool IsRateLimited = false;
public abstract void Init();
public void DeleteExecutedCommands(bool ignoreWaitCheck = false)
{
LogDebug("Deleting executed commands...");
if (!CanProcessNextDeleteCommands() && !ignoreWaitCheck)
{
LogDebug("Skipping check for completed commands - not time to be processed");
return;
}
if (ExecutedCommands.Count == 0)
{
LogDebug(" No commands to flush.");
return;
}
LogDebug($" Found {ExecutedCommands.Count} commands to flush.");
List<int> ids = new List<int>();
foreach (var command in ExecutedCommands)
{
ids.Add(command.Id);
}
_nextCheckDeleteCommands = DateTime.Now.AddSeconds(60);
TebexApi.Instance.DeleteCommands(ids.ToArray(), (code, body) =>
{
LogDebug("Successfully flushed completed commands.");
ExecutedCommands.Clear();
}, (error) =>
{
LogDebug($"Failed to flush completed commands: {error.ErrorMessage}");
}, (code, body) =>
{
LogDebug($"Unexpected error while flushing completed commands. API response code {code}. Response body follows:");
LogDebug(body);
});
}
/**
* Logs a warning to the console and game log.
*/
public abstract void LogWarning(string message);
/**
* Logs an error to the console and game log.
*/
public abstract void LogError(string message);
/**
* Logs information to the console and game log.
*/
public abstract void LogInfo(string message);
/**
* Logs debug information to the console and game log if debug mode is enabled.
*/
public abstract void LogDebug(string message);
public void OnUserConnected(string steam64Id, string ip)
{
var joinEvent = new TebexApi.TebexJoinEventInfo(steam64Id, "server.join", DateTime.Now, ip);
_eventQueue.Add(joinEvent);
// If we're already over a threshold, go ahead and send the events.
if (_eventQueue.Count > 10)
{
ProcessJoinQueue();
}
}
public class TebexConfig
{
// Enables additional debug logging, which may show raw user info in console.
public bool DebugMode = false;
// Automatically sends detected issues to Tebex
public bool AutoReportingEnabled = true;
//public bool AllowGui = false;
public string SecretKey = "your-secret-key-here";
public int CacheLifetime = 30;
//#if RUST
[JsonProperty(PropertyName = "VIP Notes Enabled")]
public bool VipNotesEnabled { get; set; } = false;
[JsonProperty(PropertyName = "VIP Codes")]
public List<string> VipCodes { get; set; } = new List<string>();
[JsonProperty(PropertyName = "VIP Groups")]
public List<string> VipGroups { get; set; } = new List<string>();
[JsonProperty(PropertyName = "Note Spawn Chance")]
public float NoteSpawnChance { get; set; } = 0.02f; // 2%
[JsonProperty(PropertyName = "Note Cooldown (Seconds)")]
public float NoteCooldown { get; set; } = 600; // 10 minutes
[JsonProperty(PropertyName = "Note Messages")]
public Dictionary<string, List<string>> NoteMessages { get; set; } = new Dictionary<string, List<string>>
{
{
"en", new List<string>
{
"Hey {0}, grab your exclusive 10% OFF VIP offer with code {2} at {1}! Limited time only!",
"{0}, seize your special discount! Use code {2} at {1} for a limited time offer!",
"Special surprise for you, {0}! Use code {2} at {1} for 10% off and enjoy your VIP benefits!"
}
},
};
//#endif
}
public class Cache
{
public static Cache Instance => _cacheInstance.Value;
private static readonly Lazy<Cache> _cacheInstance = new Lazy<Cache>(() => new Cache());
private static Dictionary<string, CachedObject> _cache = new Dictionary<string, CachedObject>();
public CachedObject Get(string key)
{
if (_cache.ContainsKey(key))
{
return _cache[key];
}
return null;
}
public void Set(string key, CachedObject obj)
{
_cache[key] = obj;
}
public bool HasValid(string key)
{
return _cache.ContainsKey(key) && !_cache[key].HasExpired();
}
public void Clear()
{
_cache.Clear();
}
public void Remove(string key)
{
_cache.Remove(key);
}
}
public class CachedObject
{
public object Value { get; private set; }
private DateTime _expires;
public CachedObject(object obj, int minutesValid)
{
Value = obj;
_expires = DateTime.Now.AddMinutes(minutesValid);
}
public bool HasExpired()
{
return DateTime.Now > _expires;
}
}
/** Callback type to use /information response */
public delegate void FetchStoreInfoResponse(TebexApi.TebexStoreInfo info);
/**
* Returns the store's /information payload. Info is cached according to configured cache lifetime.
*/
public void FetchStoreInfo(FetchStoreInfoResponse response)
{
if (Cache.Instance.HasValid("information"))
{
response?.Invoke((TebexApi.TebexStoreInfo)Cache.Instance.Get("information").Value);
}
else
{
TebexApi.Instance.Information((code, body) =>
{
var storeInfo = JsonConvert.DeserializeObject<TebexApi.TebexStoreInfo>(body);
if (storeInfo == null)
{
ReportAutoTriageEvent(new TebexTriage.AutoTriageEvent());
LogError("Failed to parse fetched store information: ");
LogError(body);