返回 7 个源码总览

Tebex-Rust

Tebex Rust 插件源码

这个东西是什么

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);