using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using Newtonsoft.Json; using UnityEngine; using UnityEngine.Networking; namespace Oxide.Plugins { [Info("Imgur API", "MJSU", "1.0.2")] [Description("Allows plugins to upload images to imgur")] internal class ImgurApi : CovalencePlugin { #region Class Fields private PluginConfig _pluginConfig; //Plugin Config private const string ClientIdUrl = "https://api.imgur.com/oauth2/addclient"; private const string UploadImageUrl = "https://api.imgur.com/3/upload"; private const string DeleteImageUrl = "https://api.imgur.com/3/image/{0}"; private const string CreateAlbumUrl = "https://api.imgur.com/3/album"; private const string DeleteAlbumUrl = "https://api.imgur.com/3/album/{0}"; private const string AlbumLink = "https://imgur.com/a/{0}"; private GameObject _go; private ImgurBehavior _behavior; #endregion #region Setup & Loading protected override void LoadDefaultConfig() { PrintWarning("Loading Default Config"); } protected override void LoadConfig() { base.LoadConfig(); Config.Settings.DefaultValueHandling = DefaultValueHandling.Populate; _pluginConfig = AdditionalConfig(Config.ReadObject()); Config.WriteObject(_pluginConfig); } private PluginConfig AdditionalConfig(PluginConfig config) { return config; } private void OnServerInitialized() { _go = new GameObject(); _behavior = _go.AddComponent(); } private void Unload() { _behavior.StopAllCoroutines(); GameObject.Destroy(_go); } #endregion #region API private void UploadImage(byte[] image, Action> callback, string title = null, string description = null) { if (_pluginConfig.ClientId == ClientIdUrl) { PrintError($"Please set your Imgur Client Id in the config!"); return; } _behavior.StartCoroutine(HandleUploadImage(image, response => { callback(response.ToHash()); }, title, description)); } private void DeleteSingleImage(string deleteHash, Action> callback) { if (_pluginConfig.ClientId == ClientIdUrl) { PrintError($"Please set your Imgur Client Id in the config!"); return; } _behavior.StartCoroutine(HandleDeleteSingleImage(deleteHash, callback)); } private void UploadAlbum(List> images, Action>> callback, string title = null, string description = null) { if (_pluginConfig.ClientId == ClientIdUrl) { PrintError($"Please set your Imgur Client Id in the config!"); return; } _behavior.StartCoroutine(HandleUploadAlbum(images, callback, title, description)); } private void DeleteAlbum(string deleteHash, Action> callback) { if (_pluginConfig.ClientId == ClientIdUrl) { PrintError($"Please set your Imgur Client Id in the config!"); return; } _behavior.StartCoroutine(HandleDeleteAlbum(deleteHash, callback)); } #endregion #region Handlers private IEnumerator HandleUploadImage(byte[] image, Action> callback, string title, string description) { List data = new List { new MultipartFormDataSection("image", Convert.ToBase64String(image)), new MultipartFormDataSection("type", "base64") }; if (!string.IsNullOrEmpty(title)) { data.Add(new MultipartFormDataSection("title", title)); } if (!string.IsNullOrEmpty(description)) { data.Add(new MultipartFormDataSection("description", description)); } yield return HandleImgurRequest(UploadImageUrl, data, callback); } private IEnumerator HandleDeleteSingleImage(string deleteHash, Action> callback) { yield return HandleImgurRequest(string.Format(DeleteImageUrl, deleteHash), new List(), response => { callback(response.ToHash()); }); } private IEnumerator HandleUploadAlbum(List> images, Action>> callback, string title = null, string description = null) { List> albumImages = new List>(); foreach (Hash uploadData in images) { byte[] image = uploadData["Image"] as byte[]; string imageTitle = uploadData["Title"] as string; string imageDescription = uploadData["Description"] as string; yield return HandleUploadImage(image, img => { albumImages.Add(img); }, imageTitle, imageDescription); yield return new WaitForSeconds(1); } List data = albumImages .Where(a => a.Success) .Select(response => new MultipartFormDataSection("deletehashes[]", response.Data.DeleteHash)) .Cast() .ToList(); if (!string.IsNullOrEmpty(title)) { data.Add(new MultipartFormDataSection("title", title)); } if (!string.IsNullOrEmpty(description)) { data.Add(new MultipartFormDataSection("description", description)); } data.Add(new MultipartFormDataSection("type", "base64")); yield return HandleImgurRequest(CreateAlbumUrl, data, response => { Hash> album = new Hash> { ["Album"] = response.ToHash() }; for (int index = 0; index < albumImages.Count; index++) { ApiResponse image = albumImages[index]; album[$"Image{index}"] = image.ToHash(); } callback(album); }); } private IEnumerator HandleDeleteAlbum(string deleteHash, Action> callback) { yield return HandleImgurRequest(string.Format(DeleteAlbumUrl, deleteHash), new List(), response => { callback(response.ToHash()); }); } #endregion #region Send Methods private IEnumerator HandleImgurRequest(string url, List data, Action> action) { UnityWebRequest www = UnityWebRequest.Post(url, data); www.SetRequestHeader("Authorization", $"Client-ID {_pluginConfig.ClientId}"); yield return www.SendWebRequest(); ApiResponse response; if (www.isNetworkError || www.isHttpError) { response = new ApiResponse { Success = false, Status = (int) www.responseCode, Errors = new[] {new ErrorMessage {Detail = www.error}} }; } else { response = JsonConvert.DeserializeObject>(www.downloadHandler.text); } action(response); } #endregion #region Behavior private class ImgurBehavior : MonoBehaviour { } #endregion #region Classes private class PluginConfig { [DefaultValue(ClientIdUrl)] [JsonProperty(PropertyName = "Imgur Client ID")] public string ClientId { get; set; } } private class ApiResponse { [JsonProperty(PropertyName = "status")] public int Status { get; set; } [JsonProperty(PropertyName = "success")] public bool Success { get; set; } [JsonProperty(PropertyName = "data")] public T Data { get; set; } [JsonProperty(PropertyName = "errors")] public ErrorMessage[] Errors { get; set; } public Hash ToHash() { object data; BaseDataResponse response = Data as BaseDataResponse; if (response != null) { data = response.ToHash(); } else { data = Data; } return new Hash { [nameof(Status)] = Status, [nameof(Success)] = Success, [nameof(Data)] = data, [nameof(Errors)] = Errors?.Select(e => e.ToHash()).ToList() }; } } private class ErrorMessage { [JsonProperty(PropertyName = "id")] public string Id { get; set; } [JsonProperty(PropertyName = "code")] public string Code { get; set; } [JsonProperty(PropertyName = "status")] public string Status { get; set; } [JsonProperty(PropertyName = "detail")] public string Detail { get; set; } public Hash ToHash() { return new Hash { [nameof(Id)] = Id, [nameof(Code)] = Code, [nameof(Status)] = Status, [nameof(Detail)] = Detail }; } } private abstract class BaseDataResponse { public abstract Hash ToHash(); } private class UploadResponse : BaseDataResponse { [JsonProperty(PropertyName = "id")] public string Id { get; set; } [JsonProperty(PropertyName = "deletehash")] public string DeleteHash { get; set; } [JsonProperty(PropertyName = "link")] public string Link { get; set; } public override Hash ToHash() { return new Hash { [nameof(Id)] = Id, [nameof(DeleteHash)] = DeleteHash, [nameof(Link)] = Link }; } } private class AlbumResponse : BaseDataResponse { [JsonProperty(PropertyName = "id")] public string Id { get; set; } [JsonProperty(PropertyName = "deletehash")] public string DeleteHash { get; set; } public string Link => string.Format(AlbumLink, Id); public override Hash ToHash() { return new Hash { [nameof(Id)] = Id, [nameof(DeleteHash)] = DeleteHash, [nameof(Link)] = Link }; } } #endregion } }