First actual commit
added sources to repository
This commit is contained in:
157
RedditSharp/Things/AuthenticatedUser.cs
Normal file
157
RedditSharp/Things/AuthenticatedUser.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace RedditSharp.Things
|
||||
{
|
||||
public class AuthenticatedUser : RedditUser
|
||||
{
|
||||
private const string ModeratorUrl = "/reddits/mine/moderator.json";
|
||||
private const string UnreadMessagesUrl = "/message/unread.json?mark=true&limit=25";
|
||||
private const string ModQueueUrl = "/r/mod/about/modqueue.json";
|
||||
private const string UnmoderatedUrl = "/r/mod/about/unmoderated.json";
|
||||
private const string ModMailUrl = "/message/moderator.json";
|
||||
private const string MessagesUrl = "/message/messages.json";
|
||||
private const string InboxUrl = "/message/inbox.json";
|
||||
private const string SentUrl = "/message/sent.json";
|
||||
|
||||
public new AuthenticatedUser Init(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, json, webAgent);
|
||||
JsonConvert.PopulateObject(json["name"] == null ? json["data"].ToString() : json.ToString(), this,
|
||||
reddit.JsonSerializerSettings);
|
||||
return this;
|
||||
}
|
||||
public async new Task<AuthenticatedUser> InitAsync(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, json, webAgent);
|
||||
await Task.Factory.StartNew(() => JsonConvert.PopulateObject(json["name"] == null ? json["data"].ToString() : json.ToString(), this,
|
||||
reddit.JsonSerializerSettings));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void CommonInit(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
base.Init(reddit, json, webAgent);
|
||||
}
|
||||
|
||||
public Listing<Subreddit> ModeratorSubreddits
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<Subreddit>(Reddit, ModeratorUrl, WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Thing> UnreadMessages
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<Thing>(Reddit, UnreadMessagesUrl, WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<VotableThing> ModerationQueue
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<VotableThing>(Reddit, ModQueueUrl, WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Post> UnmoderatedLinks
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<Post>(Reddit, UnmoderatedUrl, WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<PrivateMessage> ModMail
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<PrivateMessage>(Reddit, ModMailUrl, WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<PrivateMessage> PrivateMessages
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<PrivateMessage>(Reddit, MessagesUrl, WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<PrivateMessage> Inbox
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<PrivateMessage>(Reddit, InboxUrl, WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<PrivateMessage> Sent
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<PrivateMessage>(Reddit, SentUrl, WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
#region Obsolete Getter Methods
|
||||
|
||||
[Obsolete("Use ModeratorSubreddits property instead")]
|
||||
public Listing<Subreddit> GetModeratorReddits()
|
||||
{
|
||||
return ModeratorSubreddits;
|
||||
}
|
||||
|
||||
[Obsolete("Use UnreadMessages property instead")]
|
||||
public Listing<Thing> GetUnreadMessages()
|
||||
{
|
||||
return UnreadMessages;
|
||||
}
|
||||
|
||||
[Obsolete("Use ModerationQueue property instead")]
|
||||
public Listing<VotableThing> GetModerationQueue()
|
||||
{
|
||||
return new Listing<VotableThing>(Reddit, ModQueueUrl, WebAgent);
|
||||
}
|
||||
|
||||
public Listing<Post> GetUnmoderatedLinks()
|
||||
{
|
||||
return new Listing<Post>(Reddit, UnmoderatedUrl, WebAgent);
|
||||
}
|
||||
|
||||
[Obsolete("Use ModMail property instead")]
|
||||
public Listing<PrivateMessage> GetModMail()
|
||||
{
|
||||
return new Listing<PrivateMessage>(Reddit, ModMailUrl, WebAgent);
|
||||
}
|
||||
|
||||
[Obsolete("Use PrivateMessages property instead")]
|
||||
public Listing<PrivateMessage> GetPrivateMessages()
|
||||
{
|
||||
return new Listing<PrivateMessage>(Reddit, MessagesUrl, WebAgent);
|
||||
}
|
||||
|
||||
[Obsolete("Use Inbox property instead")]
|
||||
public Listing<PrivateMessage> GetInbox()
|
||||
{
|
||||
return new Listing<PrivateMessage>(Reddit, InboxUrl, WebAgent);
|
||||
}
|
||||
|
||||
#endregion Obsolete Getter Methods
|
||||
|
||||
[JsonProperty("modhash")]
|
||||
public string Modhash { get; set; }
|
||||
|
||||
[JsonProperty("has_mail")]
|
||||
public bool HasMail { get; set; }
|
||||
|
||||
[JsonProperty("has_mod_mail")]
|
||||
public bool HasModMail { get; set; }
|
||||
}
|
||||
}
|
||||
313
RedditSharp/Things/Comment.cs
Normal file
313
RedditSharp/Things/Comment.cs
Normal file
@@ -0,0 +1,313 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Authentication;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace RedditSharp.Things
|
||||
{
|
||||
public class Comment : VotableThing
|
||||
{
|
||||
private const string CommentUrl = "/api/comment";
|
||||
private const string DistinguishUrl = "/api/distinguish";
|
||||
private const string EditUserTextUrl = "/api/editusertext";
|
||||
private const string RemoveUrl = "/api/remove";
|
||||
private const string SetAsReadUrl = "/api/read_message";
|
||||
|
||||
[JsonIgnore]
|
||||
private Reddit Reddit { get; set; }
|
||||
[JsonIgnore]
|
||||
private IWebAgent WebAgent { get; set; }
|
||||
|
||||
public Comment Init(Reddit reddit, JToken json, IWebAgent webAgent, Thing sender)
|
||||
{
|
||||
var data = CommonInit(reddit, json, webAgent, sender);
|
||||
ParseComments(reddit, json, webAgent, sender);
|
||||
JsonConvert.PopulateObject(data.ToString(), this, reddit.JsonSerializerSettings);
|
||||
return this;
|
||||
}
|
||||
public async Task<Comment> InitAsync(Reddit reddit, JToken json, IWebAgent webAgent, Thing sender)
|
||||
{
|
||||
var data = CommonInit(reddit, json, webAgent, sender);
|
||||
await ParseCommentsAsync(reddit, json, webAgent, sender);
|
||||
await Task.Factory.StartNew(() => JsonConvert.PopulateObject(data.ToString(), this, reddit.JsonSerializerSettings));
|
||||
return this;
|
||||
}
|
||||
|
||||
private JToken CommonInit(Reddit reddit, JToken json, IWebAgent webAgent, Thing sender)
|
||||
{
|
||||
base.Init(reddit, webAgent, json);
|
||||
var data = json["data"];
|
||||
Reddit = reddit;
|
||||
WebAgent = webAgent;
|
||||
this.Parent = sender;
|
||||
|
||||
// Handle Reddit's API being horrible
|
||||
if (data["context"] != null)
|
||||
{
|
||||
var context = data["context"].Value<string>();
|
||||
LinkId = context.Split('/')[4];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private void ParseComments(Reddit reddit, JToken data, IWebAgent webAgent, Thing sender)
|
||||
{
|
||||
// Parse sub comments
|
||||
var replies = data["data"]["replies"];
|
||||
var subComments = new List<Comment>();
|
||||
if (replies != null && replies.Count() > 0)
|
||||
{
|
||||
foreach (var comment in replies["data"]["children"])
|
||||
subComments.Add(new Comment().Init(reddit, comment, webAgent, sender));
|
||||
}
|
||||
Comments = subComments.ToArray();
|
||||
}
|
||||
|
||||
private async Task ParseCommentsAsync(Reddit reddit, JToken data, IWebAgent webAgent, Thing sender)
|
||||
{
|
||||
// Parse sub comments
|
||||
var replies = data["data"]["replies"];
|
||||
var subComments = new List<Comment>();
|
||||
if (replies != null && replies.Count() > 0)
|
||||
{
|
||||
foreach (var comment in replies["data"]["children"])
|
||||
subComments.Add(await new Comment().InitAsync(reddit, comment, webAgent, sender));
|
||||
}
|
||||
Comments = subComments.ToArray();
|
||||
}
|
||||
|
||||
[JsonProperty("author")]
|
||||
public string Author { get; set; }
|
||||
[JsonProperty("banned_by")]
|
||||
public string BannedBy { get; set; }
|
||||
[JsonProperty("body")]
|
||||
public string Body { get; set; }
|
||||
[JsonProperty("body_html")]
|
||||
public string BodyHtml { get; set; }
|
||||
[JsonProperty("parent_id")]
|
||||
public string ParentId { get; set; }
|
||||
[JsonProperty("subreddit")]
|
||||
public string Subreddit { get; set; }
|
||||
[JsonProperty("approved_by")]
|
||||
public string ApprovedBy { get; set; }
|
||||
[JsonProperty("author_flair_css_class")]
|
||||
public string AuthorFlairCssClass { get; set; }
|
||||
[JsonProperty("author_flair_text")]
|
||||
public string AuthorFlairText { get; set; }
|
||||
[JsonProperty("gilded")]
|
||||
public int Gilded { get; set; }
|
||||
[JsonProperty("link_id")]
|
||||
public string LinkId { get; set; }
|
||||
[JsonProperty("link_title")]
|
||||
public string LinkTitle { get; set; }
|
||||
[JsonProperty("num_reports")]
|
||||
public int? NumReports { get; set; }
|
||||
[JsonProperty("distinguished")]
|
||||
[JsonConverter(typeof(DistinguishConverter))]
|
||||
public DistinguishType Distinguished { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public IList<Comment> Comments { get; private set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Thing Parent { get; internal set; }
|
||||
|
||||
public override string Shortlink
|
||||
{
|
||||
get
|
||||
{
|
||||
// Not really a "short" link, but you can't actually use short links for comments
|
||||
string linkId = "";
|
||||
int index = this.LinkId.IndexOf('_');
|
||||
if (index > -1)
|
||||
{
|
||||
linkId = this.LinkId.Substring(index + 1);
|
||||
}
|
||||
|
||||
return String.Format("{0}://{1}/r/{2}/comments/{3}/_/{4}",
|
||||
RedditSharp.WebAgent.Protocol, RedditSharp.WebAgent.RootDomain,
|
||||
this.Subreddit, this.Parent != null ? this.Parent.Id : linkId, this.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public Comment Reply(string message)
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new AuthenticationException("No user logged in.");
|
||||
var request = WebAgent.CreatePost(CommentUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
text = message,
|
||||
thing_id = FullName,
|
||||
uh = Reddit.User.Modhash,
|
||||
api_type = "json"
|
||||
//r = Subreddit
|
||||
});
|
||||
stream.Close();
|
||||
try
|
||||
{
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JObject.Parse(data);
|
||||
if (json["json"]["ratelimit"] != null)
|
||||
throw new RateLimitException(TimeSpan.FromSeconds(json["json"]["ratelimit"].ValueOrDefault<double>()));
|
||||
return new Comment().Init(Reddit, json["json"]["data"]["things"][0], WebAgent, this);
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
var error = new StreamReader(ex.Response.GetResponseStream()).ReadToEnd();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Distinguish(DistinguishType distinguishType)
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new AuthenticationException("No user logged in.");
|
||||
var request = WebAgent.CreatePost(DistinguishUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
string how;
|
||||
switch (distinguishType)
|
||||
{
|
||||
case DistinguishType.Admin:
|
||||
how = "admin";
|
||||
break;
|
||||
case DistinguishType.Moderator:
|
||||
how = "yes";
|
||||
break;
|
||||
case DistinguishType.None:
|
||||
how = "no";
|
||||
break;
|
||||
default:
|
||||
how = "special";
|
||||
break;
|
||||
}
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
how,
|
||||
id = Id,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JObject.Parse(data);
|
||||
if (json["jquery"].Count(i => i[0].Value<int>() == 11 && i[1].Value<int>() == 12) == 0)
|
||||
throw new AuthenticationException("You are not permitted to distinguish this comment.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the text in this comment with the input text.
|
||||
/// </summary>
|
||||
/// <param name="newText">The text to replace the comment's contents</param>
|
||||
public void EditText(string newText)
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new Exception("No user logged in.");
|
||||
|
||||
var request = WebAgent.CreatePost(EditUserTextUrl);
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
api_type = "json",
|
||||
text = newText,
|
||||
thing_id = FullName,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
var response = request.GetResponse();
|
||||
var result = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
JToken json = JToken.Parse(result);
|
||||
if (json["json"].ToString().Contains("\"errors\": []"))
|
||||
Body = newText;
|
||||
else
|
||||
throw new Exception("Error editing text.");
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
RemoveImpl(false);
|
||||
}
|
||||
|
||||
public void RemoveSpam()
|
||||
{
|
||||
RemoveImpl(true);
|
||||
}
|
||||
|
||||
private void RemoveImpl(bool spam)
|
||||
{
|
||||
var request = WebAgent.CreatePost(RemoveUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
id = FullName,
|
||||
spam = spam,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
|
||||
public void SetAsRead()
|
||||
{
|
||||
var request = WebAgent.CreatePost(SetAsReadUrl);
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
id = FullName,
|
||||
uh = Reddit.User.Modhash,
|
||||
api_type = "json"
|
||||
});
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
}
|
||||
|
||||
public enum DistinguishType
|
||||
{
|
||||
Moderator,
|
||||
Admin,
|
||||
Special,
|
||||
None
|
||||
}
|
||||
|
||||
internal class DistinguishConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(DistinguishType) || objectType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
var token = JToken.Load(reader);
|
||||
var value = token.Value<string>();
|
||||
if (value == null)
|
||||
return DistinguishType.None;
|
||||
switch (value)
|
||||
{
|
||||
case "moderator": return DistinguishType.Moderator;
|
||||
case "admin": return DistinguishType.Admin;
|
||||
case "special": return DistinguishType.Special;
|
||||
default: return DistinguishType.None;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var d = (DistinguishType)value;
|
||||
if (d == DistinguishType.None)
|
||||
{
|
||||
writer.WriteNull();
|
||||
return;
|
||||
}
|
||||
writer.WriteValue(d.ToString().ToLower());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
40
RedditSharp/Things/CreatedThing.cs
Normal file
40
RedditSharp/Things/CreatedThing.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace RedditSharp.Things
|
||||
{
|
||||
public class CreatedThing : Thing
|
||||
{
|
||||
private Reddit Reddit { get; set; }
|
||||
|
||||
protected CreatedThing Init(Reddit reddit, JToken json)
|
||||
{
|
||||
CommonInit(reddit, json);
|
||||
JsonConvert.PopulateObject(json["data"].ToString(), this, reddit.JsonSerializerSettings);
|
||||
return this;
|
||||
}
|
||||
protected async Task<CreatedThing> InitAsync(Reddit reddit, JToken json)
|
||||
{
|
||||
CommonInit(reddit, json);
|
||||
await Task.Factory.StartNew(() => JsonConvert.PopulateObject(json["data"].ToString(), this, reddit.JsonSerializerSettings));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void CommonInit(Reddit reddit, JToken json)
|
||||
{
|
||||
base.Init(json);
|
||||
Reddit = reddit;
|
||||
}
|
||||
|
||||
|
||||
[JsonProperty("created")]
|
||||
[JsonConverter(typeof(UnixTimestampConverter))]
|
||||
public DateTime Created { get; set; }
|
||||
|
||||
[JsonProperty("created_utc")]
|
||||
[JsonConverter(typeof(UnixTimestampConverter))]
|
||||
public DateTime CreatedUTC { get; set; }
|
||||
}
|
||||
}
|
||||
344
RedditSharp/Things/Post.cs
Normal file
344
RedditSharp/Things/Post.cs
Normal file
@@ -0,0 +1,344 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Authentication;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace RedditSharp.Things
|
||||
{
|
||||
public class Post : VotableThing
|
||||
{
|
||||
private const string CommentUrl = "/api/comment";
|
||||
private const string RemoveUrl = "/api/remove";
|
||||
private const string DelUrl = "/api/del";
|
||||
private const string GetCommentsUrl = "/comments/{0}.json";
|
||||
private const string ApproveUrl = "/api/approve";
|
||||
private const string EditUserTextUrl = "/api/editusertext";
|
||||
private const string HideUrl = "/api/hide";
|
||||
private const string UnhideUrl = "/api/unhide";
|
||||
private const string SetFlairUrl = "/api/flair";
|
||||
private const string MarkNSFWUrl = "/api/marknsfw";
|
||||
private const string UnmarkNSFWUrl = "/api/unmarknsfw";
|
||||
private const string ContestModeUrl = "/api/set_contest_mode";
|
||||
|
||||
[JsonIgnore]
|
||||
private Reddit Reddit { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private IWebAgent WebAgent { get; set; }
|
||||
|
||||
public Post Init(Reddit reddit, JToken post, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, post, webAgent);
|
||||
JsonConvert.PopulateObject(post["data"].ToString(), this, reddit.JsonSerializerSettings);
|
||||
return this;
|
||||
}
|
||||
public async Task<Post> InitAsync(Reddit reddit, JToken post, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, post, webAgent);
|
||||
await Task.Factory.StartNew(() => JsonConvert.PopulateObject(post["data"].ToString(), this, reddit.JsonSerializerSettings));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void CommonInit(Reddit reddit, JToken post, IWebAgent webAgent)
|
||||
{
|
||||
base.Init(reddit, webAgent, post);
|
||||
Reddit = reddit;
|
||||
WebAgent = webAgent;
|
||||
}
|
||||
|
||||
[JsonProperty("author")]
|
||||
public string AuthorName { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public RedditUser Author
|
||||
{
|
||||
get
|
||||
{
|
||||
return Reddit.GetUser(AuthorName);
|
||||
}
|
||||
}
|
||||
|
||||
public Comment[] Comments
|
||||
{
|
||||
get
|
||||
{
|
||||
return ListComments().ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("approved_by")]
|
||||
public string ApprovedBy { get; set; }
|
||||
|
||||
[JsonProperty("author_flair_css_class")]
|
||||
public string AuthorFlairCssClass { get; set; }
|
||||
|
||||
[JsonProperty("author_flair_text")]
|
||||
public string AuthorFlairText { get; set; }
|
||||
|
||||
[JsonProperty("banned_by")]
|
||||
public string BannedBy { get; set; }
|
||||
|
||||
[JsonProperty("domain")]
|
||||
public string Domain { get; set; }
|
||||
|
||||
[JsonProperty("edited")]
|
||||
public bool Edited { get; set; }
|
||||
|
||||
[JsonProperty("is_self")]
|
||||
public bool IsSelfPost { get; set; }
|
||||
|
||||
[JsonProperty("link_flair_css_class")]
|
||||
public string LinkFlairCssClass { get; set; }
|
||||
|
||||
[JsonProperty("link_flair_text")]
|
||||
public string LinkFlairText { get; set; }
|
||||
|
||||
[JsonProperty("num_comments")]
|
||||
public int CommentCount { get; set; }
|
||||
|
||||
[JsonProperty("over_18")]
|
||||
public bool NSFW { get; set; }
|
||||
|
||||
[JsonProperty("permalink")]
|
||||
[JsonConverter(typeof(UrlParser))]
|
||||
public Uri Permalink { get; set; }
|
||||
|
||||
[JsonProperty("score")]
|
||||
public int Score { get; set; }
|
||||
|
||||
[JsonProperty("selftext")]
|
||||
public string SelfText { get; set; }
|
||||
|
||||
[JsonProperty("selftext_html")]
|
||||
public string SelfTextHtml { get; set; }
|
||||
|
||||
[JsonProperty("subreddit")]
|
||||
public string Subreddit { get; set; }
|
||||
|
||||
[JsonProperty("thumbnail")]
|
||||
[JsonConverter(typeof(UrlParser))]
|
||||
public Uri Thumbnail { get; set; }
|
||||
|
||||
[JsonProperty("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
[JsonConverter(typeof(UrlParser))]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonProperty("num_reports")]
|
||||
public int? Reports { get; set; }
|
||||
|
||||
public Comment Comment(string message)
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new AuthenticationException("No user logged in.");
|
||||
var request = WebAgent.CreatePost(CommentUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
text = message,
|
||||
thing_id = FullName,
|
||||
uh = Reddit.User.Modhash,
|
||||
api_type = "json"
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JObject.Parse(data);
|
||||
if (json["json"]["ratelimit"] != null)
|
||||
throw new RateLimitException(TimeSpan.FromSeconds(json["json"]["ratelimit"].ValueOrDefault<double>()));
|
||||
return new Comment().Init(Reddit, json["json"]["data"]["things"][0], WebAgent, this);
|
||||
}
|
||||
|
||||
private string SimpleAction(string endpoint)
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new AuthenticationException("No user logged in.");
|
||||
var request = WebAgent.CreatePost(endpoint);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
id = FullName,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
return data;
|
||||
}
|
||||
|
||||
private string SimpleActionToggle(string endpoint, bool value)
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new AuthenticationException("No user logged in.");
|
||||
var request = WebAgent.CreatePost(endpoint);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
id = FullName,
|
||||
state = value,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
return data;
|
||||
}
|
||||
|
||||
public void Approve()
|
||||
{
|
||||
var data = SimpleAction(ApproveUrl);
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
RemoveImpl(false);
|
||||
}
|
||||
|
||||
public void RemoveSpam()
|
||||
{
|
||||
RemoveImpl(true);
|
||||
}
|
||||
|
||||
private void RemoveImpl(bool spam)
|
||||
{
|
||||
var request = WebAgent.CreatePost(RemoveUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
id = FullName,
|
||||
spam = spam,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
|
||||
public void Del()
|
||||
{
|
||||
var data = SimpleAction(ApproveUrl);
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
var data = SimpleAction(HideUrl);
|
||||
}
|
||||
|
||||
public void Unhide()
|
||||
{
|
||||
var data = SimpleAction(UnhideUrl);
|
||||
}
|
||||
|
||||
public void MarkNSFW()
|
||||
{
|
||||
var data = SimpleAction(MarkNSFWUrl);
|
||||
}
|
||||
|
||||
public void UnmarkNSFW()
|
||||
{
|
||||
var data = SimpleAction(UnmarkNSFWUrl);
|
||||
}
|
||||
|
||||
public void ContestMode(bool state)
|
||||
{
|
||||
var data = SimpleActionToggle(ContestModeUrl, state);
|
||||
}
|
||||
|
||||
#region Obsolete Getter Methods
|
||||
|
||||
[Obsolete("Use Comments property instead")]
|
||||
public Comment[] GetComments()
|
||||
{
|
||||
return Comments;
|
||||
}
|
||||
|
||||
#endregion Obsolete Getter Methods
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the text in this post with the input text.
|
||||
/// </summary>
|
||||
/// <param name="newText">The text to replace the post's contents</param>
|
||||
public void EditText(string newText)
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new Exception("No user logged in.");
|
||||
if (!IsSelfPost)
|
||||
throw new Exception("Submission to edit is not a self-post.");
|
||||
|
||||
var request = WebAgent.CreatePost(EditUserTextUrl);
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
api_type = "json",
|
||||
text = newText,
|
||||
thing_id = FullName,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
var response = request.GetResponse();
|
||||
var result = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
JToken json = JToken.Parse(result);
|
||||
if (json["json"].ToString().Contains("\"errors\": []"))
|
||||
SelfText = newText;
|
||||
else
|
||||
throw new Exception("Error editing text.");
|
||||
}
|
||||
public void Update()
|
||||
{
|
||||
JToken post = Reddit.GetToken(this.Url);
|
||||
JsonConvert.PopulateObject(post["data"].ToString(), this, Reddit.JsonSerializerSettings);
|
||||
}
|
||||
|
||||
public void SetFlair(string flairText, string flairClass)
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new Exception("No user logged in.");
|
||||
|
||||
var request = WebAgent.CreatePost(SetFlairUrl);
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
api_type = "json",
|
||||
r = Subreddit,
|
||||
css_class = flairClass,
|
||||
link = FullName,
|
||||
//name = Name,
|
||||
text = flairText,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
var response = request.GetResponse();
|
||||
var result = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JToken.Parse(result);
|
||||
LinkFlairText = flairText;
|
||||
}
|
||||
|
||||
public List<Comment> ListComments(int? limit = null)
|
||||
{
|
||||
var url = string.Format(GetCommentsUrl, Id);
|
||||
|
||||
if (limit.HasValue)
|
||||
{
|
||||
var query = HttpUtility.ParseQueryString(string.Empty);
|
||||
query.Add("limit", limit.Value.ToString());
|
||||
url = string.Format("{0}?{1}", url, query);
|
||||
}
|
||||
|
||||
var request = WebAgent.CreateGet(url);
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JArray.Parse(data);
|
||||
var postJson = json.Last()["data"]["children"];
|
||||
|
||||
var comments = new List<Comment>();
|
||||
foreach (var comment in postJson)
|
||||
{
|
||||
comments.Add(new Comment().Init(Reddit, comment, WebAgent, this));
|
||||
}
|
||||
|
||||
return comments;
|
||||
}
|
||||
}
|
||||
}
|
||||
163
RedditSharp/Things/PrivateMessage.cs
Normal file
163
RedditSharp/Things/PrivateMessage.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Authentication;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace RedditSharp.Things
|
||||
{
|
||||
public class PrivateMessage : Thing
|
||||
{
|
||||
private const string SetAsReadUrl = "/api/read_message";
|
||||
private const string CommentUrl = "/api/comment";
|
||||
|
||||
private Reddit Reddit { get; set; }
|
||||
private IWebAgent WebAgent { get; set; }
|
||||
|
||||
[JsonProperty("body")]
|
||||
public string Body { get; set; }
|
||||
|
||||
[JsonProperty("body_html")]
|
||||
public string BodyHtml { get; set; }
|
||||
|
||||
[JsonProperty("was_comment")]
|
||||
public bool IsComment { get; set; }
|
||||
|
||||
[JsonProperty("created")]
|
||||
[JsonConverter(typeof(UnixTimestampConverter))]
|
||||
public DateTime Sent { get; set; }
|
||||
|
||||
[JsonProperty("created_utc")]
|
||||
[JsonConverter(typeof(UnixTimestampConverter))]
|
||||
public DateTime SentUTC { get; set; }
|
||||
|
||||
[JsonProperty("dest")]
|
||||
public string Destination { get; set; }
|
||||
|
||||
[JsonProperty("author")]
|
||||
public string Author { get; set; }
|
||||
|
||||
[JsonProperty("subreddit")]
|
||||
public string Subreddit { get; set; }
|
||||
|
||||
[JsonProperty("new")]
|
||||
public bool Unread { get; set; }
|
||||
|
||||
[JsonProperty("subject")]
|
||||
public string Subject { get; set; }
|
||||
|
||||
[JsonProperty("parent_id")]
|
||||
public string ParentID { get; set; }
|
||||
|
||||
[JsonProperty("first_message_name")]
|
||||
public string FirstMessageName { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public PrivateMessage[] Replies { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public PrivateMessage Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(ParentID))
|
||||
return null;
|
||||
var id = ParentID.Remove(0, 3);
|
||||
var listing = new Listing<PrivateMessage>(Reddit, "/message/messages/" + id + ".json", WebAgent);
|
||||
var firstMessage = listing.First();
|
||||
if (firstMessage.FullName == ParentID)
|
||||
return listing.First();
|
||||
else
|
||||
return firstMessage.Replies.First(x => x.FullName == ParentID);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<PrivateMessage> Thread
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(ParentID))
|
||||
return null;
|
||||
var id = ParentID.Remove(0, 3);
|
||||
return new Listing<PrivateMessage>(Reddit, "/message/messages/" + id + ".json", WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public PrivateMessage Init(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, json, webAgent);
|
||||
JsonConvert.PopulateObject(json["data"].ToString(), this, reddit.JsonSerializerSettings);
|
||||
return this;
|
||||
}
|
||||
public async Task<PrivateMessage> InitAsync(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, json, webAgent);
|
||||
await Task.Factory.StartNew(() => JsonConvert.PopulateObject(json["data"].ToString(), this, reddit.JsonSerializerSettings));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void CommonInit(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
base.Init(json);
|
||||
Reddit = reddit;
|
||||
WebAgent = webAgent;
|
||||
var data = json["data"];
|
||||
if (data["replies"] != null && data["replies"].Any())
|
||||
{
|
||||
if (data["replies"]["data"] != null)
|
||||
{
|
||||
if (data["replies"]["data"]["children"] != null)
|
||||
{
|
||||
var replies = new List<PrivateMessage>();
|
||||
foreach (var reply in data["replies"]["data"]["children"])
|
||||
replies.Add(new PrivateMessage().Init(reddit, reply, webAgent));
|
||||
Replies = replies.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Obsolete Getter Methods
|
||||
|
||||
[Obsolete("Use Thread property instead")]
|
||||
public Listing<PrivateMessage> GetThread()
|
||||
{
|
||||
return Thread;
|
||||
}
|
||||
|
||||
#endregion Obsolete Gettter Methods
|
||||
|
||||
public void SetAsRead()
|
||||
{
|
||||
var request = WebAgent.CreatePost(SetAsReadUrl);
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
id = FullName,
|
||||
uh = Reddit.User.Modhash,
|
||||
api_type = "json"
|
||||
});
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
|
||||
public void Reply(string message)
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new AuthenticationException("No user logged in.");
|
||||
var request = WebAgent.CreatePost(CommentUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
text = message,
|
||||
thing_id = FullName,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JObject.Parse(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
201
RedditSharp/Things/RedditUser.cs
Normal file
201
RedditSharp/Things/RedditUser.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace RedditSharp.Things
|
||||
{
|
||||
public class RedditUser : Thing
|
||||
{
|
||||
private const string OverviewUrl = "/user/{0}.json";
|
||||
private const string CommentsUrl = "/user/{0}/comments.json";
|
||||
private const string LinksUrl = "/user/{0}/submitted.json";
|
||||
private const string SubscribedSubredditsUrl = "/subreddits/mine.json";
|
||||
private const string LikedUrl = "/user/{0}/liked.json";
|
||||
private const string DislikedUrl = "/user/{0}/disliked.json";
|
||||
|
||||
private const int MAX_LIMIT = 100;
|
||||
|
||||
public RedditUser Init(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, json, webAgent);
|
||||
JsonConvert.PopulateObject(json["name"] == null ? json["data"].ToString() : json.ToString(), this,
|
||||
reddit.JsonSerializerSettings);
|
||||
return this;
|
||||
}
|
||||
public async Task<RedditUser> InitAsync(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, json, webAgent);
|
||||
await Task.Factory.StartNew(() => JsonConvert.PopulateObject(json["name"] == null ? json["data"].ToString() : json.ToString(), this,
|
||||
reddit.JsonSerializerSettings));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void CommonInit(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
base.Init(json);
|
||||
Reddit = reddit;
|
||||
WebAgent = webAgent;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
protected Reddit Reddit { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
protected IWebAgent WebAgent { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("is_gold")]
|
||||
public bool HasGold { get; set; }
|
||||
|
||||
[JsonProperty("is_mod")]
|
||||
public bool IsModerator { get; set; }
|
||||
|
||||
[JsonProperty("link_karma")]
|
||||
public int LinkKarma { get; set; }
|
||||
|
||||
[JsonProperty("comment_karma")]
|
||||
public int CommentKarma { get; set; }
|
||||
|
||||
[JsonProperty("created")]
|
||||
[JsonConverter(typeof(UnixTimestampConverter))]
|
||||
public DateTime Created { get; set; }
|
||||
|
||||
public Listing<VotableThing> Overview
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<VotableThing>(Reddit, string.Format(OverviewUrl, Name), WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Post> LikedPosts
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<Post>(Reddit, string.Format(LikedUrl, Name), WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Post> DislikedPosts
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<Post>(Reddit, string.Format(DislikedUrl, Name), WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Comment> Comments
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<Comment>(Reddit, string.Format(CommentsUrl, Name), WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Post> Posts
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<Post>(Reddit, string.Format(LinksUrl, Name), WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Subreddit> SubscribedSubreddits
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<Subreddit>(Reddit, SubscribedSubredditsUrl, WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a listing of comments from the user sorted by <paramref name="sorting"/>, from time <paramref name="fromTime"/>
|
||||
/// and limited to <paramref name="limit"/>.
|
||||
/// </summary>
|
||||
/// <param name="sorting">How to sort the comments (hot, new, top, controversial).</param>
|
||||
/// <param name="limit">How many comments to fetch per request. Max is 100.</param>
|
||||
/// <param name="fromTime">What time frame of comments to show (hour, day, week, month, year, all).</param>
|
||||
/// <returns>The listing of comments requested.</returns>
|
||||
public Listing<Comment> GetComments(Sort sorting = Sort.New, int limit = 25, FromTime fromTime = FromTime.All)
|
||||
{
|
||||
if ((limit < 1) || (limit > MAX_LIMIT))
|
||||
throw new ArgumentOutOfRangeException("limit", "Valid range: [1," + MAX_LIMIT + "]");
|
||||
string commentsUrl = string.Format(CommentsUrl, Name);
|
||||
commentsUrl += string.Format("?sort={0}&limit={1}&t={2}", Enum.GetName(typeof(Sort), sorting), limit, Enum.GetName(typeof(FromTime), fromTime));
|
||||
|
||||
return new Listing<Comment>(Reddit, commentsUrl, WebAgent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a listing of posts from the user sorted by <paramref name="sorting"/>, from time <paramref name="fromTime"/>
|
||||
/// and limited to <paramref name="limit"/>.
|
||||
/// </summary>
|
||||
/// <param name="sorting">How to sort the posts (hot, new, top, controversial).</param>
|
||||
/// <param name="limit">How many posts to fetch per request. Max is 100.</param>
|
||||
/// <param name="fromTime">What time frame of posts to show (hour, day, week, month, year, all).</param>
|
||||
/// <returns>The listing of posts requested.</returns>
|
||||
public Listing<Post> GetPosts(Sort sorting = Sort.New, int limit = 25, FromTime fromTime = FromTime.All)
|
||||
{
|
||||
if ((limit < 1) || (limit > 100))
|
||||
throw new ArgumentOutOfRangeException("limit", "Valid range: [1,100]");
|
||||
string linksUrl = string.Format(LinksUrl, Name);
|
||||
linksUrl += string.Format("?sort={0}&limit={1}&t={2}", Enum.GetName(typeof(Sort), sorting), limit, Enum.GetName(typeof(FromTime), fromTime));
|
||||
|
||||
return new Listing<Post>(Reddit, linksUrl, WebAgent);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
#region Obsolete Getter Methods
|
||||
|
||||
[Obsolete("Use Overview property instead")]
|
||||
public Listing<VotableThing> GetOverview()
|
||||
{
|
||||
return Overview;
|
||||
}
|
||||
|
||||
[Obsolete("Use Comments property instead")]
|
||||
public Listing<Comment> GetComments()
|
||||
{
|
||||
return Comments;
|
||||
}
|
||||
|
||||
[Obsolete("Use Posts property instead")]
|
||||
public Listing<Post> GetPosts()
|
||||
{
|
||||
return Posts;
|
||||
}
|
||||
|
||||
[Obsolete("Use SubscribedSubreddits property instead")]
|
||||
public Listing<Subreddit> GetSubscribedSubreddits()
|
||||
{
|
||||
return SubscribedSubreddits;
|
||||
}
|
||||
|
||||
#endregion Obsolete Getter Methods
|
||||
}
|
||||
|
||||
public enum Sort
|
||||
{
|
||||
New,
|
||||
Hot,
|
||||
Top,
|
||||
Controversial
|
||||
}
|
||||
|
||||
public enum FromTime
|
||||
{
|
||||
All,
|
||||
Year,
|
||||
Month,
|
||||
Week,
|
||||
Day,
|
||||
Hour
|
||||
}
|
||||
}
|
||||
677
RedditSharp/Things/Subreddit.cs
Normal file
677
RedditSharp/Things/Subreddit.cs
Normal file
@@ -0,0 +1,677 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Authentication;
|
||||
using System.Threading.Tasks;
|
||||
using HtmlAgilityPack;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace RedditSharp.Things
|
||||
{
|
||||
public class Subreddit : Thing
|
||||
{
|
||||
private const string SubredditPostUrl = "/r/{0}.json";
|
||||
private const string SubredditNewUrl = "/r/{0}/new.json?sort=new";
|
||||
private const string SubredditHotUrl = "/r/{0}/hot.json";
|
||||
private const string SubredditTopUrl = "/r/{0}/top.json?t={1}";
|
||||
private const string SubscribeUrl = "/api/subscribe";
|
||||
private const string GetSettingsUrl = "/r/{0}/about/edit.json";
|
||||
private const string GetReducedSettingsUrl = "/r/{0}/about.json";
|
||||
private const string ModqueueUrl = "/r/{0}/about/modqueue.json";
|
||||
private const string UnmoderatedUrl = "/r/{0}/about/unmoderated.json";
|
||||
private const string FlairTemplateUrl = "/api/flairtemplate";
|
||||
private const string ClearFlairTemplatesUrl = "/api/clearflairtemplates";
|
||||
private const string SetUserFlairUrl = "/api/flair";
|
||||
private const string StylesheetUrl = "/r/{0}/about/stylesheet.json";
|
||||
private const string UploadImageUrl = "/api/upload_sr_img";
|
||||
private const string FlairSelectorUrl = "/api/flairselector";
|
||||
private const string AcceptModeratorInviteUrl = "/api/accept_moderator_invite";
|
||||
private const string LeaveModerationUrl = "/api/unfriend";
|
||||
private const string BanUserUrl = "/api/friend";
|
||||
private const string AddModeratorUrl = "/api/friend";
|
||||
private const string AddContributorUrl = "/api/friend";
|
||||
private const string ModeratorsUrl = "/r/{0}/about/moderators.json";
|
||||
private const string FrontPageUrl = "/.json";
|
||||
private const string SubmitLinkUrl = "/api/submit";
|
||||
private const string FlairListUrl = "/r/{0}/api/flairlist.json";
|
||||
private const string CommentsUrl = "/r/{0}/comments.json";
|
||||
private const string SearchUrl = "/r/{0}/search.json?q={1}&restrict_sr=on&sort={2}&t={3}";
|
||||
|
||||
[JsonIgnore]
|
||||
private Reddit Reddit { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private IWebAgent WebAgent { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Wiki Wiki { get; private set; }
|
||||
|
||||
[JsonProperty("created")]
|
||||
[JsonConverter(typeof(UnixTimestampConverter))]
|
||||
public DateTime? Created { get; set; }
|
||||
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonProperty("description_html")]
|
||||
public string DescriptionHTML { get; set; }
|
||||
|
||||
[JsonProperty("display_name")]
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
[JsonProperty("header_img")]
|
||||
public string HeaderImage { get; set; }
|
||||
|
||||
[JsonProperty("header_title")]
|
||||
public string HeaderTitle { get; set; }
|
||||
|
||||
[JsonProperty("over18")]
|
||||
public bool? NSFW { get; set; }
|
||||
|
||||
[JsonProperty("public_description")]
|
||||
public string PublicDescription { get; set; }
|
||||
|
||||
[JsonProperty("subscribers")]
|
||||
public int? Subscribers { get; set; }
|
||||
|
||||
[JsonProperty("accounts_active")]
|
||||
public int? ActiveUsers { get; set; }
|
||||
|
||||
[JsonProperty("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
[JsonConverter(typeof(UrlParser))]
|
||||
public Uri Url { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string Name { get; set; }
|
||||
|
||||
public Listing<Post> GetTop(FromTime timePeriod)
|
||||
{
|
||||
if (Name == "/")
|
||||
{
|
||||
return new Listing<Post>(Reddit, "/top.json?t=" + Enum.GetName(typeof(FromTime), timePeriod).ToLower(), WebAgent);
|
||||
}
|
||||
return new Listing<Post>(Reddit, string.Format(SubredditTopUrl, Name, Enum.GetName(typeof(FromTime), timePeriod)).ToLower(), WebAgent);
|
||||
}
|
||||
|
||||
public Listing<Post> Posts
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Name == "/")
|
||||
return new Listing<Post>(Reddit, "/.json", WebAgent);
|
||||
return new Listing<Post>(Reddit, string.Format(SubredditPostUrl, Name), WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Comment> Comments
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Name == "/")
|
||||
return new Listing<Comment>(Reddit, "/comments.json", WebAgent);
|
||||
return new Listing<Comment>(Reddit, string.Format(CommentsUrl, Name), WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Post> New
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Name == "/")
|
||||
return new Listing<Post>(Reddit, "/new.json", WebAgent);
|
||||
return new Listing<Post>(Reddit, string.Format(SubredditNewUrl, Name), WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Post> Hot
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Name == "/")
|
||||
return new Listing<Post>(Reddit, "/.json", WebAgent);
|
||||
return new Listing<Post>(Reddit, string.Format(SubredditHotUrl, Name), WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<VotableThing> ModQueue
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<VotableThing>(Reddit, string.Format(ModqueueUrl, Name), WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Post> UnmoderatedLinks
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Listing<Post>(Reddit, string.Format(UnmoderatedUrl, Name), WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<Post> Search(string terms)
|
||||
{
|
||||
return new Listing<Post>(Reddit, string.Format(SearchUrl, Name, Uri.EscapeUriString(terms), "relevance", "all"), WebAgent);
|
||||
}
|
||||
|
||||
public SubredditSettings Settings
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new AuthenticationException("No user logged in.");
|
||||
try
|
||||
{
|
||||
var request = WebAgent.CreateGet(string.Format(GetSettingsUrl, Name));
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JObject.Parse(data);
|
||||
return new SubredditSettings(this, Reddit, json, WebAgent);
|
||||
}
|
||||
catch // TODO: More specific catch
|
||||
{
|
||||
// Do it unauthed
|
||||
var request = WebAgent.CreateGet(string.Format(GetReducedSettingsUrl, Name));
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JObject.Parse(data);
|
||||
return new SubredditSettings(this, Reddit, json, WebAgent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UserFlairTemplate[] UserFlairTemplates // Hacky, there isn't a proper endpoint for this
|
||||
{
|
||||
get
|
||||
{
|
||||
var request = WebAgent.CreatePost(FlairSelectorUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
name = Reddit.User.Name,
|
||||
r = Name,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var document = new HtmlDocument();
|
||||
document.LoadHtml(data);
|
||||
if (document.DocumentNode.Descendants("div").First().Attributes["error"] != null)
|
||||
throw new InvalidOperationException("This subreddit does not allow users to select flair.");
|
||||
var templateNodes = document.DocumentNode.Descendants("li");
|
||||
var list = new List<UserFlairTemplate>();
|
||||
foreach (var node in templateNodes)
|
||||
{
|
||||
list.Add(new UserFlairTemplate
|
||||
{
|
||||
CssClass = node.Descendants("span").First().Attributes["class"].Value.Split(' ')[1],
|
||||
Text = node.Descendants("span").First().InnerText
|
||||
});
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public SubredditStyle Stylesheet
|
||||
{
|
||||
get
|
||||
{
|
||||
var request = WebAgent.CreateGet(string.Format(StylesheetUrl, Name));
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JToken.Parse(data);
|
||||
return new SubredditStyle(Reddit, this, json, WebAgent);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ModeratorUser> Moderators
|
||||
{
|
||||
get
|
||||
{
|
||||
var request = WebAgent.CreateGet(string.Format(ModeratorsUrl, Name));
|
||||
var response = request.GetResponse();
|
||||
var responseString = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JObject.Parse(responseString);
|
||||
var type = json["kind"].ToString();
|
||||
if (type != "UserList")
|
||||
throw new FormatException("Reddit responded with an object that is not a user listing.");
|
||||
var data = json["data"];
|
||||
var mods = data["children"].ToArray();
|
||||
var result = new ModeratorUser[mods.Length];
|
||||
for (var i = 0; i < mods.Length; i++)
|
||||
{
|
||||
var mod = new ModeratorUser(Reddit, mods[i]);
|
||||
result[i] = mod;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public Subreddit Init(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, json, webAgent);
|
||||
JsonConvert.PopulateObject(json["data"].ToString(), this, reddit.JsonSerializerSettings);
|
||||
SetName();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public async Task<Subreddit> InitAsync(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, json, webAgent);
|
||||
await Task.Factory.StartNew(() => JsonConvert.PopulateObject(json["data"].ToString(), this, reddit.JsonSerializerSettings));
|
||||
SetName();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private void SetName()
|
||||
{
|
||||
Name = Url.ToString();
|
||||
if (Name.StartsWith("/r/"))
|
||||
Name = Name.Substring(3);
|
||||
if (Name.StartsWith("r/"))
|
||||
Name = Name.Substring(2);
|
||||
Name = Name.TrimEnd('/');
|
||||
}
|
||||
|
||||
private void CommonInit(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
base.Init(json);
|
||||
Reddit = reddit;
|
||||
WebAgent = webAgent;
|
||||
Wiki = new Wiki(reddit, this, webAgent);
|
||||
}
|
||||
|
||||
public static Subreddit GetRSlashAll(Reddit reddit)
|
||||
{
|
||||
var rSlashAll = new Subreddit
|
||||
{
|
||||
DisplayName = "/r/all",
|
||||
Title = "/r/all",
|
||||
Url = new Uri("/r/all", UriKind.Relative),
|
||||
Name = "all",
|
||||
Reddit = reddit,
|
||||
WebAgent = reddit._webAgent
|
||||
};
|
||||
return rSlashAll;
|
||||
}
|
||||
|
||||
public static Subreddit GetFrontPage(Reddit reddit)
|
||||
{
|
||||
var frontPage = new Subreddit
|
||||
{
|
||||
DisplayName = "Front Page",
|
||||
Title = "reddit: the front page of the internet",
|
||||
Url = new Uri("/", UriKind.Relative),
|
||||
Name = "/",
|
||||
Reddit = reddit,
|
||||
WebAgent = reddit._webAgent
|
||||
};
|
||||
return frontPage;
|
||||
}
|
||||
|
||||
public void Subscribe()
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new AuthenticationException("No user logged in.");
|
||||
var request = WebAgent.CreatePost(SubscribeUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
action = "sub",
|
||||
sr = FullName,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
// Discard results
|
||||
}
|
||||
|
||||
public void Unsubscribe()
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new AuthenticationException("No user logged in.");
|
||||
var request = WebAgent.CreatePost(SubscribeUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
action = "unsub",
|
||||
sr = FullName,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
// Discard results
|
||||
}
|
||||
|
||||
public void ClearFlairTemplates(FlairType flairType)
|
||||
{
|
||||
var request = WebAgent.CreatePost(ClearFlairTemplatesUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
flair_type = flairType == FlairType.Link ? "LINK_FLAIR" : "USER_FLAIR",
|
||||
uh = Reddit.User.Modhash,
|
||||
r = Name
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
|
||||
public void AddFlairTemplate(string cssClass, FlairType flairType, string text, bool userEditable)
|
||||
{
|
||||
var request = WebAgent.CreatePost(FlairTemplateUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
css_class = cssClass,
|
||||
flair_type = flairType == FlairType.Link ? "LINK_FLAIR" : "USER_FLAIR",
|
||||
text = text,
|
||||
text_editable = userEditable,
|
||||
uh = Reddit.User.Modhash,
|
||||
r = Name,
|
||||
api_type = "json"
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JToken.Parse(data);
|
||||
}
|
||||
|
||||
public string GetFlairText(string user)
|
||||
{
|
||||
var request = WebAgent.CreateGet(String.Format(FlairListUrl + "?name=" + user, Name));
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JToken.Parse(data);
|
||||
return (string)json["users"][0]["flair_text"];
|
||||
}
|
||||
|
||||
public string GetFlairCssClass(string user)
|
||||
{
|
||||
var request = WebAgent.CreateGet(String.Format(FlairListUrl + "?name=" + user, Name));
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JToken.Parse(data);
|
||||
return (string)json["users"][0]["flair_css_class"];
|
||||
}
|
||||
|
||||
public void SetUserFlair(string user, string cssClass, string text)
|
||||
{
|
||||
var request = WebAgent.CreatePost(SetUserFlairUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
css_class = cssClass,
|
||||
text = text,
|
||||
uh = Reddit.User.Modhash,
|
||||
r = Name,
|
||||
name = user
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
|
||||
public void UploadHeaderImage(string name, ImageType imageType, byte[] file)
|
||||
{
|
||||
var request = WebAgent.CreatePost(UploadImageUrl);
|
||||
var formData = new MultipartFormBuilder(request);
|
||||
formData.AddDynamic(new
|
||||
{
|
||||
name,
|
||||
uh = Reddit.User.Modhash,
|
||||
r = Name,
|
||||
formid = "image-upload",
|
||||
img_type = imageType == ImageType.PNG ? "png" : "jpg",
|
||||
upload = "",
|
||||
header = 1
|
||||
});
|
||||
formData.AddFile("file", "foo.png", file, imageType == ImageType.PNG ? "image/png" : "image/jpeg");
|
||||
formData.Finish();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
// TODO: Detect errors
|
||||
}
|
||||
|
||||
public void AddModerator(string user)
|
||||
{
|
||||
var request = WebAgent.CreatePost(AddModeratorUrl);
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
api_type = "json",
|
||||
uh = Reddit.User.Modhash,
|
||||
r = Name,
|
||||
type = "moderator",
|
||||
name = user
|
||||
});
|
||||
var response = request.GetResponse();
|
||||
var result = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
|
||||
public void AcceptModeratorInvite()
|
||||
{
|
||||
var request = WebAgent.CreatePost(AcceptModeratorInviteUrl);
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
api_type = "json",
|
||||
uh = Reddit.User.Modhash,
|
||||
r = Name
|
||||
});
|
||||
var response = request.GetResponse();
|
||||
var result = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
|
||||
public void RemoveModerator(string id)
|
||||
{
|
||||
var request = WebAgent.CreatePost(LeaveModerationUrl);
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
api_type = "json",
|
||||
uh = Reddit.User.Modhash,
|
||||
r = Name,
|
||||
type = "moderator",
|
||||
id
|
||||
});
|
||||
var response = request.GetResponse();
|
||||
var result = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "/r/" + DisplayName;
|
||||
}
|
||||
|
||||
public void AddContributor(string user)
|
||||
{
|
||||
var request = WebAgent.CreatePost(AddContributorUrl);
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
api_type = "json",
|
||||
uh = Reddit.User.Modhash,
|
||||
r = Name,
|
||||
type = "contributor",
|
||||
name = user
|
||||
});
|
||||
var response = request.GetResponse();
|
||||
var result = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
|
||||
public void RemoveContributor(string id)
|
||||
{
|
||||
var request = WebAgent.CreatePost(LeaveModerationUrl);
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
api_type = "json",
|
||||
uh = Reddit.User.Modhash,
|
||||
r = Name,
|
||||
type = "contributor",
|
||||
id
|
||||
});
|
||||
var response = request.GetResponse();
|
||||
var result = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
|
||||
public void BanUser(string user, string reason)
|
||||
{
|
||||
var request = WebAgent.CreatePost(BanUserUrl);
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
api_type = "json",
|
||||
uh = Reddit.User.Modhash,
|
||||
r = Name,
|
||||
type = "banned",
|
||||
id = "#banned",
|
||||
name = user,
|
||||
note = reason,
|
||||
action = "add",
|
||||
container = FullName
|
||||
});
|
||||
var response = request.GetResponse();
|
||||
var result = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
|
||||
private Post Submit(SubmitData data)
|
||||
{
|
||||
if (Reddit.User == null)
|
||||
throw new RedditException("No user logged in.");
|
||||
var request = WebAgent.CreatePost(SubmitLinkUrl);
|
||||
|
||||
WebAgent.WritePostBody(request.GetRequestStream(), data);
|
||||
|
||||
var response = request.GetResponse();
|
||||
var result = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JToken.Parse(result);
|
||||
|
||||
ICaptchaSolver solver = Reddit.CaptchaSolver;
|
||||
if (json["json"]["errors"].Any() && json["json"]["errors"][0][0].ToString() == "BAD_CAPTCHA"
|
||||
&& solver != null)
|
||||
{
|
||||
data.Iden = json["json"]["captcha"].ToString();
|
||||
CaptchaResponse captchaResponse = solver.HandleCaptcha(new Captcha(data.Iden));
|
||||
|
||||
// We throw exception due to this method being expected to return a valid Post object, but we cannot
|
||||
// if we got a Captcha error.
|
||||
if (captchaResponse.Cancel)
|
||||
throw new CaptchaFailedException("Captcha verification failed when submitting " + data.Kind + " post");
|
||||
|
||||
data.Captcha = captchaResponse.Answer;
|
||||
return Submit(data);
|
||||
}
|
||||
else if (json["json"]["errors"].Any() && json["json"]["errors"][0][0].ToString() == "ALREADY_SUB")
|
||||
{
|
||||
throw new DuplicateLinkException(String.Format("Post failed when submitting. The following link has already been submitted: {0}", SubmitLinkUrl));
|
||||
}
|
||||
|
||||
return new Post().Init(Reddit, json["json"], WebAgent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submits a link post in the current subreddit using the logged-in user
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the submission</param>
|
||||
/// <param name="url">The url of the submission link</param>
|
||||
public Post SubmitPost(string title, string url, string captchaId = "", string captchaAnswer = "", bool resubmit = false)
|
||||
{
|
||||
return
|
||||
Submit(
|
||||
new LinkData
|
||||
{
|
||||
Subreddit = Name,
|
||||
UserHash = Reddit.User.Modhash,
|
||||
Title = title,
|
||||
URL = url,
|
||||
Resubmit = resubmit,
|
||||
Iden = captchaId,
|
||||
Captcha = captchaAnswer
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submits a text post in the current subreddit using the logged-in user
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the submission</param>
|
||||
/// <param name="text">The raw markdown text of the submission</param>
|
||||
public Post SubmitTextPost(string title, string text, string captchaId = "", string captchaAnswer = "")
|
||||
{
|
||||
return
|
||||
Submit(
|
||||
new TextData
|
||||
{
|
||||
Subreddit = Name,
|
||||
UserHash = Reddit.User.Modhash,
|
||||
Title = title,
|
||||
Text = text,
|
||||
Iden = captchaId,
|
||||
Captcha = captchaAnswer
|
||||
});
|
||||
}
|
||||
|
||||
#region Obsolete Getter Methods
|
||||
|
||||
[Obsolete("Use Posts property instead")]
|
||||
public Listing<Post> GetPosts()
|
||||
{
|
||||
return Posts;
|
||||
}
|
||||
|
||||
[Obsolete("Use New property instead")]
|
||||
public Listing<Post> GetNew()
|
||||
{
|
||||
return New;
|
||||
}
|
||||
|
||||
[Obsolete("Use Hot property instead")]
|
||||
public Listing<Post> GetHot()
|
||||
{
|
||||
return Hot;
|
||||
}
|
||||
|
||||
[Obsolete("Use ModQueue property instead")]
|
||||
public Listing<VotableThing> GetModQueue()
|
||||
{
|
||||
return ModQueue;
|
||||
}
|
||||
|
||||
[Obsolete("Use UnmoderatedLinks property instead")]
|
||||
public Listing<Post> GetUnmoderatedLinks()
|
||||
{
|
||||
return UnmoderatedLinks;
|
||||
}
|
||||
|
||||
[Obsolete("Use Settings property instead")]
|
||||
public SubredditSettings GetSettings()
|
||||
{
|
||||
return Settings;
|
||||
}
|
||||
|
||||
[Obsolete("Use UserFlairTemplates property instead")]
|
||||
public UserFlairTemplate[] GetUserFlairTemplates() // Hacky, there isn't a proper endpoint for this
|
||||
{
|
||||
return UserFlairTemplates;
|
||||
}
|
||||
|
||||
[Obsolete("Use Stylesheet property instead")]
|
||||
public SubredditStyle GetStylesheet()
|
||||
{
|
||||
return Stylesheet;
|
||||
}
|
||||
|
||||
[Obsolete("Use Moderators property instead")]
|
||||
public IEnumerable<ModeratorUser> GetModerators()
|
||||
{
|
||||
return Moderators;
|
||||
}
|
||||
|
||||
#endregion Obsolete Getter Methods
|
||||
}
|
||||
}
|
||||
113
RedditSharp/Things/Thing.cs
Normal file
113
RedditSharp/Things/Thing.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RedditSharp.Things
|
||||
{
|
||||
public class Thing
|
||||
{
|
||||
public static Thing Parse(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
var kind = json["kind"].ValueOrDefault<string>();
|
||||
switch (kind)
|
||||
{
|
||||
case "t1":
|
||||
return new Comment().Init(reddit, json, webAgent, null);
|
||||
case "t2":
|
||||
return new RedditUser().Init(reddit, json, webAgent);
|
||||
case "t3":
|
||||
return new Post().Init(reddit, json, webAgent);
|
||||
case "t4":
|
||||
return new PrivateMessage().Init(reddit, json, webAgent);
|
||||
case "t5":
|
||||
return new Subreddit().Init(reddit, json, webAgent);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// if we can't determine the type of thing by "kind", try by type
|
||||
public static Thing Parse<T>(Reddit reddit, JToken json, IWebAgent webAgent) where T : Thing
|
||||
{
|
||||
Thing result = Parse(reddit, json, webAgent);
|
||||
if (result == null)
|
||||
{
|
||||
if (typeof(T) == typeof(WikiPageRevision))
|
||||
{
|
||||
return new WikiPageRevision().Init(reddit, json, webAgent);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void Init(JToken json)
|
||||
{
|
||||
if (json == null)
|
||||
return;
|
||||
var data = json["name"] == null ? json["data"] : json;
|
||||
FullName = data["name"].ValueOrDefault<string>();
|
||||
Id = data["id"].ValueOrDefault<string>();
|
||||
Kind = json["kind"].ValueOrDefault<string>();
|
||||
FetchedAt = DateTime.Now;
|
||||
}
|
||||
|
||||
public virtual string Shortlink
|
||||
{
|
||||
get { return "http://redd.it/" + Id; }
|
||||
}
|
||||
|
||||
public string Id { get; set; }
|
||||
public string FullName { get; set; }
|
||||
public string Kind { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The time at which this object was fetched from reddit servers.
|
||||
/// </summary>
|
||||
public DateTime FetchedAt { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time since last fetch from reddit servers.
|
||||
/// </summary>
|
||||
public TimeSpan TimeSinceFetch
|
||||
{
|
||||
get
|
||||
{
|
||||
return DateTime.Now - FetchedAt;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<Thing> ParseAsync(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
var kind = json["kind"].ValueOrDefault<string>();
|
||||
switch (kind)
|
||||
{
|
||||
case "t1":
|
||||
return await new Comment().InitAsync(reddit, json, webAgent, null);
|
||||
case "t2":
|
||||
return await new RedditUser().InitAsync(reddit, json, webAgent);
|
||||
case "t3":
|
||||
return await new Post().InitAsync(reddit, json, webAgent);
|
||||
case "t4":
|
||||
return await new PrivateMessage().InitAsync(reddit, json, webAgent);
|
||||
case "t5":
|
||||
return await new Subreddit().InitAsync(reddit, json, webAgent);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// if we can't determine the type of thing by "kind", try by type
|
||||
public static async Task<Thing> ParseAsync<T>(Reddit reddit, JToken json, IWebAgent webAgent) where T : Thing
|
||||
{
|
||||
Thing result = await ParseAsync(reddit, json, webAgent);
|
||||
if (result == null)
|
||||
{
|
||||
if (typeof(T) == typeof(WikiPageRevision))
|
||||
{
|
||||
return await new WikiPageRevision().InitAsync(reddit, json, webAgent);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
162
RedditSharp/Things/VotableThing.cs
Normal file
162
RedditSharp/Things/VotableThing.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace RedditSharp.Things
|
||||
{
|
||||
public class VotableThing : CreatedThing
|
||||
{
|
||||
public enum VoteType
|
||||
{
|
||||
Upvote = 1,
|
||||
None = 0,
|
||||
Downvote = -1
|
||||
}
|
||||
|
||||
private const string VoteUrl = "/api/vote";
|
||||
private const string SaveUrl = "/api/save";
|
||||
private const string UnsaveUrl = "/api/unsave";
|
||||
|
||||
[JsonIgnore]
|
||||
private IWebAgent WebAgent { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private Reddit Reddit { get; set; }
|
||||
|
||||
protected VotableThing Init(Reddit reddit, IWebAgent webAgent, JToken json)
|
||||
{
|
||||
CommonInit(reddit, webAgent, json);
|
||||
JsonConvert.PopulateObject(json["data"].ToString(), this, Reddit.JsonSerializerSettings);
|
||||
return this;
|
||||
}
|
||||
protected async Task<VotableThing> InitAsync(Reddit reddit, IWebAgent webAgent, JToken json)
|
||||
{
|
||||
CommonInit(reddit, webAgent, json);
|
||||
await Task.Factory.StartNew(() => JsonConvert.PopulateObject(json["data"].ToString(), this, Reddit.JsonSerializerSettings));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void CommonInit(Reddit reddit, IWebAgent webAgent, JToken json)
|
||||
{
|
||||
base.Init(reddit, json);
|
||||
Reddit = reddit;
|
||||
WebAgent = webAgent;
|
||||
}
|
||||
|
||||
[JsonProperty("downs")]
|
||||
public int Downvotes { get; set; }
|
||||
[JsonProperty("ups")]
|
||||
public int Upvotes { get; set; }
|
||||
[JsonProperty("saved")]
|
||||
public bool Saved { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the logged in user has upvoted this.
|
||||
/// False if they have not.
|
||||
/// Null if they have not cast a vote.
|
||||
/// </summary>
|
||||
[JsonProperty("likes")]
|
||||
public bool? Liked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vote for the current VotableThing.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public VoteType Vote
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (this.Liked)
|
||||
{
|
||||
case true: return VoteType.Upvote;
|
||||
case false: return VoteType.Downvote;
|
||||
|
||||
default: return VoteType.None;
|
||||
}
|
||||
}
|
||||
set { this.SetVote(value); }
|
||||
}
|
||||
|
||||
public void Upvote()
|
||||
{
|
||||
this.SetVote(VoteType.Upvote);
|
||||
}
|
||||
|
||||
public void Downvote()
|
||||
{
|
||||
this.SetVote(VoteType.Downvote);
|
||||
}
|
||||
|
||||
public void SetVote(VoteType type)
|
||||
{
|
||||
if (this.Vote == type) return;
|
||||
|
||||
var request = WebAgent.CreatePost(VoteUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
dir = (int)type,
|
||||
id = FullName,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
|
||||
if (Liked == true) Upvotes--;
|
||||
if (Liked == false) Downvotes--;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case VoteType.Upvote: Liked = true; Upvotes++; return;
|
||||
case VoteType.None: Liked = null; return;
|
||||
case VoteType.Downvote: Liked = false; Downvotes++; return;
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var request = WebAgent.CreatePost(SaveUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
id = FullName,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
Saved = true;
|
||||
}
|
||||
|
||||
public void Unsave()
|
||||
{
|
||||
var request = WebAgent.CreatePost(UnsaveUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
id = FullName,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
Saved = false;
|
||||
}
|
||||
|
||||
public void ClearVote()
|
||||
{
|
||||
var request = WebAgent.CreatePost(VoteUrl);
|
||||
var stream = request.GetRequestStream();
|
||||
WebAgent.WritePostBody(stream, new
|
||||
{
|
||||
dir = 0,
|
||||
id = FullName,
|
||||
uh = Reddit.User.Modhash
|
||||
});
|
||||
stream.Close();
|
||||
var response = request.GetResponse();
|
||||
var data = WebAgent.GetResponseString(response.GetResponseStream());
|
||||
}
|
||||
}
|
||||
}
|
||||
48
RedditSharp/Things/WikiPageRevision.cs
Normal file
48
RedditSharp/Things/WikiPageRevision.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace RedditSharp.Things
|
||||
{
|
||||
public class WikiPageRevision : Thing
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
new public string Id { get; private set; }
|
||||
|
||||
[JsonProperty("timestamp")]
|
||||
[JsonConverter(typeof(UnixTimestampConverter))]
|
||||
public DateTime? TimeStamp { get; set; }
|
||||
|
||||
[JsonProperty("reason")]
|
||||
public string Reason { get; private set; }
|
||||
|
||||
[JsonProperty("page")]
|
||||
public string Page { get; private set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public RedditUser Author { get; set; }
|
||||
|
||||
protected internal WikiPageRevision() { }
|
||||
|
||||
internal WikiPageRevision Init(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, json, webAgent);
|
||||
JsonConvert.PopulateObject(json.ToString(), this, reddit.JsonSerializerSettings);
|
||||
return this;
|
||||
}
|
||||
|
||||
internal async Task<WikiPageRevision> InitAsync(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
CommonInit(reddit, json, webAgent);
|
||||
await Task.Factory.StartNew(() => JsonConvert.PopulateObject(json.ToString(), this, reddit.JsonSerializerSettings));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void CommonInit(Reddit reddit, JToken json, IWebAgent webAgent)
|
||||
{
|
||||
base.Init(json);
|
||||
Author = new RedditUser().Init(reddit, json["author"], webAgent);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user