Repository: SirCmpwn/RedditSharp
Branch: master
Commit: a5e1e6afb729
Files: 62
Total size: 205.8 KB
Directory structure:
gitextract_p863b84s/
├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── RedditSharp/
│ ├── AuthProvider.cs
│ ├── Captcha.cs
│ ├── CaptchaFailedException.cs
│ ├── CaptchaResponse.cs
│ ├── ConsoleCaptchaSolver.cs
│ ├── Domain.cs
│ ├── DuplicateLinkException.cs
│ ├── Extensions.cs
│ ├── FlairTemplate.cs
│ ├── FlairType.cs
│ ├── ICaptchaSolver.cs
│ ├── IWebAgent.cs
│ ├── LinkData.cs
│ ├── Listing.cs
│ ├── ModActionType.cs
│ ├── ModeratorPermission.cs
│ ├── ModeratorUser.cs
│ ├── MultipartFormBuilder.cs
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── RateLimitException.cs
│ ├── Reddit.cs
│ ├── RedditAPINameAttribute.cs
│ ├── RedditException.cs
│ ├── RedditSharp.csproj
│ ├── SpamFilterSettings.cs
│ ├── SubmitData.cs
│ ├── SubredditImage.cs
│ ├── SubredditSettings.cs
│ ├── SubredditStyle.cs
│ ├── TBUserNote.cs
│ ├── TextData.cs
│ ├── Things/
│ │ ├── AuthenticatedUser.cs
│ │ ├── Comment.cs
│ │ ├── Contributor.cs
│ │ ├── CreatedThing.cs
│ │ ├── ModAction.cs
│ │ ├── Post.cs
│ │ ├── PrivateMessage.cs
│ │ ├── RedditUser.cs
│ │ ├── Subreddit.cs
│ │ ├── Thing.cs
│ │ ├── VotableThing.cs
│ │ └── WikiPageRevision.cs
│ ├── ToolBoxUserNotes.cs
│ ├── ToolBoxUserNotesException.cs
│ ├── UnixTimeStamp.cs
│ ├── UnixTimestampConverter.cs
│ ├── UrlParser.cs
│ ├── Utils/
│ │ └── DateTimeExtensions.cs
│ ├── WebAgent.cs
│ ├── Wiki.cs
│ ├── WikiPage.cs
│ └── WikiPageSettings.cs
├── RedditSharp.sln
└── TestRedditSharp/
├── App.config
├── Program.cs
├── Properties/
│ └── AssemblyInfo.cs
└── TestRedditSharp.csproj
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
*.cs text diff=csharp
*.sln text eol=crlf
*.csproj text eol=crlf
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
================================================
FILE: .gitignore
================================================
bin/
obj/
*.user
*.userprefs
*.suo
*.pidb
_ReSharper*/
TestResults/
*.mdf
*.psess
*.vsp
# StyleCop files
StyleCop.Cache
================================================
FILE: LICENSE
================================================
Copyright (c) 2013 Drew DeVault
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# RedditSharp
A partial implementation of the [Reddit](http://reddit.com) API. Includes support for many API endpoints, as well as
LINQ-style paging of results.
```csharp
var reddit = new Reddit();
var user = reddit.LogIn("username", "password");
var subreddit = reddit.GetSubreddit("/r/example");
subreddit.Subscribe();
foreach (var post in subreddit.New.Take(25))
{
if (post.Title == "What is my karma?")
{
// Note: This is an example. Bots are not permitted to cast votes automatically.
post.Upvote();
var comment = post.Comment(string.Format("You have {0} link karma!", post.Author.LinkKarma));
comment.Distinguish(DistinguishType.Moderator);
}
}
```
**Important note**: Make sure you use `.Take(int)` when working with pagable content. For example, don't do this:
```csharp
var all = reddit.RSlashAll;
foreach (var post in all) // BAD
{
// ...
}
```
This will cause you to page through everything that has ever been posted on Reddit. Better:
```csharp
var all = reddit.RSlashAll;
foreach (var post in all.Take(25))
{
// ...
}
```
## Development
RedditSharp is developed with the following workflow:
1. Nothing happens for weeks/months/years
2. Someone needs it to do something it doesn't already do
3. That person implements that something and submits a pull request
4. Repeat
If it doesn't have a feature that you want it to have, add it! The code isn't that scary.
================================================
FILE: RedditSharp/AuthProvider.cs
================================================
using System;
using System.Net;
using System.Security.Authentication;
using System.Text;
using Newtonsoft.Json.Linq;
using RedditSharp.Things;
namespace RedditSharp
{
public class AuthProvider
{
private const string AccessUrl = "https://ssl.reddit.com/api/v1/access_token";
private const string OauthGetMeUrl = "https://oauth.reddit.com/api/v1/me";
private const string RevokeUrl = "https://www.reddit.com/api/v1/revoke_token";
public static string OAuthToken { get; set; }
public static string RefreshToken { get; set; }
[Flags]
public enum Scope
{
none = 0x0,
identity = 0x1,
edit = 0x2,
flair = 0x4,
history = 0x8,
modconfig = 0x10,
modflair = 0x20,
modlog = 0x40,
modposts = 0x80,
modwiki = 0x100,
mysubreddits = 0x200,
privatemessages = 0x400,
read = 0x800,
report = 0x1000,
save = 0x2000,
submit = 0x4000,
subscribe = 0x8000,
vote = 0x10000,
wikiedit = 0x20000,
wikiread = 0x40000
}
private IWebAgent _webAgent;
private readonly string _redirectUri;
private readonly string _clientId;
private readonly string _clientSecret;
///
/// Allows use of reddit's OAuth interface, using an app set up at https://ssl.reddit.com/prefs/apps/.
///
/// Granted by reddit as part of app.
/// Granted by reddit as part of app.
/// Selected as part of app. Reddit will send users back here.
public AuthProvider(string clientId, string clientSecret, string redirectUri)
{
_clientId = clientId;
_clientSecret = clientSecret;
_redirectUri = redirectUri;
_webAgent = new WebAgent();
}
///
/// Allows use of reddit's OAuth interface, using an app set up at https://ssl.reddit.com/prefs/apps/.
///
/// Granted by reddit as part of app.
/// Granted by reddit as part of app.
/// Selected as part of app. Reddit will send users back here.
/// Implementation of IWebAgent to use to make requests.
public AuthProvider(string clientId, string clientSecret, string redirectUri,IWebAgent agent)
{
_clientId = clientId;
_clientSecret = clientSecret;
_redirectUri = redirectUri;
_webAgent = agent;
}
///
/// Creates the reddit OAuth2 Url to redirect the user to for authorization.
///
/// Used to verify that the user received is the user that was sent
/// Determines what actions can be performed against the user.
/// Set to true for access lasting longer than one hour.
///
public string GetAuthUrl(string state, Scope scope, bool permanent = false)
{
return String.Format("https://ssl.reddit.com/api/v1/authorize?client_id={0}&response_type=code&state={1}&redirect_uri={2}&duration={3}&scope={4}", _clientId, state, _redirectUri, permanent ? "permanent" : "temporary", scope.ToString().Replace(" ",""));
}
///
/// Gets the OAuth token for the user associated with the provided code.
///
/// Sent by reddit as a parameter in the return uri.
/// Set to true for refresh requests.
///
public string GetOAuthToken(string code, bool isRefresh = false)
{
if (Type.GetType("Mono.Runtime") != null)
ServicePointManager.ServerCertificateValidationCallback = (s, c, ch, ssl) => true;
_webAgent.Cookies = new CookieContainer();
var request = _webAgent.CreatePost(AccessUrl);
request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.Default.GetBytes(_clientId + ":" + _clientSecret));
var stream = request.GetRequestStream();
if (isRefresh)
{
_webAgent.WritePostBody(stream, new
{
grant_type = "refresh_token",
refresh_token = code
});
}
else
{
_webAgent.WritePostBody(stream, new
{
grant_type = "authorization_code",
code,
redirect_uri = _redirectUri
});
}
stream.Close();
var json = _webAgent.ExecuteRequest(request);
if (json["access_token"] != null)
{
if (json["refresh_token"] != null)
RefreshToken = json["refresh_token"].ToString();
OAuthToken = json["access_token"].ToString();
return json["access_token"].ToString();
}
throw new AuthenticationException("Could not log in.");
}
///
/// Gets the OAuth token for the user.
///
/// The username.
/// The user's password.
/// The access token
public string GetOAuthToken(string username, string password)
{
if (Type.GetType("Mono.Runtime") != null)
ServicePointManager.ServerCertificateValidationCallback = (s, c, ch, ssl) => true;
_webAgent.Cookies = new CookieContainer();
var request = _webAgent.CreatePost(AccessUrl);
request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.Default.GetBytes(_clientId + ":" + _clientSecret));
var stream = request.GetRequestStream();
_webAgent.WritePostBody(stream, new
{
grant_type = "password",
username,
password,
redirect_uri = _redirectUri
});
stream.Close();
var json = _webAgent.ExecuteRequest(request);
if (json["access_token"] != null)
{
if (json["refresh_token"] != null)
RefreshToken = json["refresh_token"].ToString();
OAuthToken = json["access_token"].ToString();
return json["access_token"].ToString();
}
throw new AuthenticationException("Could not log in.");
}
public void RevokeToken(string token, bool isRefresh)
{
string tokenType = isRefresh ? "refresh_token" : "access_token";
var request = _webAgent.CreatePost(RevokeUrl);
request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.Default.GetBytes(_clientId + ":" + _clientSecret));
var stream = request.GetRequestStream();
_webAgent.WritePostBody(stream, new
{
token = token,
token_type = tokenType
});
stream.Close();
_webAgent.ExecuteRequest(request);
}
///
/// Gets a user authenticated by OAuth2.
///
/// Obtained using GetOAuthToken
///
[Obsolete("Reddit.InitOrUpdateUser is preferred")]
public AuthenticatedUser GetUser(string accessToken)
{
var request = _webAgent.CreateGet(OauthGetMeUrl);
request.Headers["Authorization"] = String.Format("bearer {0}", accessToken);
var response = (HttpWebResponse)request.GetResponse();
var result = _webAgent.GetResponseString(response.GetResponseStream());
var thingjson = "{\"kind\": \"t2\", \"data\": " + result + "}";
var json = JObject.Parse(thingjson);
return new AuthenticatedUser().Init(new Reddit(), json, _webAgent);
}
}
}
================================================
FILE: RedditSharp/Captcha.cs
================================================
using System;
namespace RedditSharp
{
public struct Captcha
{
private const string UrlFormat = "http://www.reddit.com/captcha/{0}";
public readonly string Id;
public readonly Uri Url;
internal Captcha(string id)
{
Id = id;
Url = new Uri(string.Format(UrlFormat, Id), UriKind.Absolute);
}
}
}
================================================
FILE: RedditSharp/CaptchaFailedException.cs
================================================
using System;
namespace RedditSharp
{
public class CaptchaFailedException : RedditException
{
public CaptchaFailedException()
{
}
public CaptchaFailedException(string message)
: base(message)
{
}
public CaptchaFailedException(string message, Exception inner)
: base(message, inner)
{
}
}
}
================================================
FILE: RedditSharp/CaptchaResponse.cs
================================================
namespace RedditSharp
{
public class CaptchaResponse
{
public readonly string Answer;
public bool Cancel { get { return string.IsNullOrEmpty(Answer); } }
public CaptchaResponse(string answer = null)
{
Answer = answer;
}
}
}
================================================
FILE: RedditSharp/ConsoleCaptchaSolver.cs
================================================
using System;
namespace RedditSharp
{
public class ConsoleCaptchaSolver : ICaptchaSolver
{
public CaptchaResponse HandleCaptcha(Captcha captcha)
{
Console.WriteLine("Captcha required! The captcha ID is {0}", captcha.Id);
Console.WriteLine("You can find the captcha image at this url: {0}", captcha.Url);
Console.WriteLine("Please input your captcha response or empty string to cancel:");
var response = Console.ReadLine();
CaptchaResponse captchaResponse = new CaptchaResponse(string.IsNullOrEmpty(response) ? null : response);
return captchaResponse;
}
}
}
================================================
FILE: RedditSharp/Domain.cs
================================================
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RedditSharp.Things;
namespace RedditSharp
{
public class Domain
{
private const string DomainPostUrl = "/domain/{0}.json";
private const string DomainNewUrl = "/domain/{0}/new.json?sort=new";
private const string DomainHotUrl = "/domain/{0}/hot.json";
private const string FrontPageUrl = "/.json";
[JsonIgnore]
private Reddit Reddit { get; set; }
[JsonIgnore]
private IWebAgent WebAgent { get; set; }
[JsonIgnore]
public string Name { get; set; }
public Listing Posts
{
get
{
return new Listing(Reddit, string.Format(DomainPostUrl, Name), WebAgent);
}
}
public Listing New
{
get
{
return new Listing(Reddit, string.Format(DomainNewUrl, Name), WebAgent);
}
}
public Listing Hot
{
get
{
return new Listing(Reddit, string.Format(DomainHotUrl, Name), WebAgent);
}
}
protected internal Domain(Reddit reddit, Uri domain, IWebAgent webAgent)
{
Reddit = reddit;
WebAgent = webAgent;
Name = domain.Host;
}
public override string ToString()
{
return "/domain/" + Name;
}
}
}
================================================
FILE: RedditSharp/DuplicateLinkException.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace RedditSharp
{
///
/// Exception that gets thrown if you try and submit a duplicate link to a SubReddit
///
public class DuplicateLinkException : RedditException
{
public DuplicateLinkException()
{
}
public DuplicateLinkException(string message)
: base(message)
{
}
public DuplicateLinkException(string message, Exception inner)
: base(message, inner)
{
}
}
}
================================================
FILE: RedditSharp/Extensions.cs
================================================
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
namespace RedditSharp
{
public static class Extensions
{
public static T ValueOrDefault(this IEnumerable enumerable)
{
if (enumerable == null)
return default(T);
return enumerable.Value();
}
}
}
================================================
FILE: RedditSharp/FlairTemplate.cs
================================================
namespace RedditSharp
{
public class UserFlairTemplate // TODO: Consider using this class to set templates as well
{
public string Text { get; set; }
public string CssClass { get; set; }
}
}
================================================
FILE: RedditSharp/FlairType.cs
================================================
namespace RedditSharp
{
public enum FlairType
{
Link,
User
}
}
================================================
FILE: RedditSharp/ICaptchaSolver.cs
================================================
namespace RedditSharp
{
public interface ICaptchaSolver
{
CaptchaResponse HandleCaptcha(Captcha captcha);
}
}
================================================
FILE: RedditSharp/IWebAgent.cs
================================================
using System.IO;
using System.Net;
using Newtonsoft.Json.Linq;
namespace RedditSharp
{
public interface IWebAgent
{
CookieContainer Cookies { get; set; }
string AuthCookie { get; set; }
string AccessToken { get; set; }
HttpWebRequest CreateRequest(string url, string method);
HttpWebRequest CreateGet(string url);
HttpWebRequest CreatePost(string url);
string GetResponseString(Stream stream);
void WritePostBody(Stream stream, object data, params string[] additionalFields);
JToken CreateAndExecuteRequest(string url);
JToken ExecuteRequest(HttpWebRequest request);
}
}
================================================
FILE: RedditSharp/LinkData.cs
================================================
namespace RedditSharp
{
internal class LinkData : SubmitData
{
[RedditAPIName("extension")]
internal string Extension { get; set; }
[RedditAPIName("url")]
internal string URL { get; set; }
internal LinkData()
{
Extension = "json";
Kind = "link";
}
}
}
================================================
FILE: RedditSharp/Listing.cs
================================================
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using RedditSharp.Things;
namespace RedditSharp
{
public enum Sorting
{
Relevance,
New,
Top,
Comments
}
public enum TimeSorting
{
All,
Hour,
Day,
Week,
Month,
Year
}
public class Listing : IEnumerable where T : Thing
{
///
/// Gets the default number of listings returned per request
///
internal const int DefaultListingPerRequest = 25;
private IWebAgent WebAgent { get; set; }
private Reddit Reddit { get; set; }
private string Url { get; set; }
///
/// Creates a new Listing instance
///
///
///
///
internal Listing(Reddit reddit, string url, IWebAgent webAgent)
{
WebAgent = webAgent;
Reddit = reddit;
Url = url;
}
///
/// Returns an enumerator that iterates through a collection, using the specified number of listings per
/// request and optionally the maximum number of listings
///
/// The number of listings to be returned per request
/// The maximum number of listings to return
///
public IEnumerator GetEnumerator(int limitPerRequest, int maximumLimit = -1)
{
return new ListingEnumerator(this, limitPerRequest, maximumLimit);
}
///
/// Returns an enumerator that iterates through a collection, using the default number of listings per request
///
///
public IEnumerator GetEnumerator()
{
return GetEnumerator(DefaultListingPerRequest);
}
///
/// Returns an enumerator that iterates through a collection
///
///
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
///
/// Returns an IEnumerable instance which will return the specified maximum number of listings
///
///
///
public IEnumerable GetListing(int maximumLimit)
{
return GetListing(maximumLimit, DefaultListingPerRequest);
}
///
/// Returns an IEnumerable instance which will return the specified maximum number of listings
/// with the limited number per request
///
///
///
///
public IEnumerable GetListing(int maximumLimit, int limitPerRequest)
{
// Get the enumerator with the specified maximum and per request limits
var enumerator = GetEnumerator(limitPerRequest, maximumLimit);
return GetEnumerator(enumerator);
}
///
/// Converts an IEnumerator instance to an IEnumerable
///
///
///
private static IEnumerable GetEnumerator(IEnumerator enumerator)
{
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
}
#pragma warning disable 0693
private class ListingEnumerator : IEnumerator where T : Thing
{
private Listing Listing { get; set; }
private int CurrentPageIndex { get; set; }
private string After { get; set; }
private string Before { get; set; }
private Thing[] CurrentPage { get; set; }
private int Count { get; set; }
private int LimitPerRequest { get; set; }
private int MaximumLimit { get; set; }
///
/// Creates a new ListingEnumerator instance
///
///
/// The number of listings to be returned per request. -1 will exclude this parameter and use the Reddit default (25)
/// The maximum number of listings to return, -1 will not add a limit
public ListingEnumerator(Listing listing, int limitPerRequest, int maximumLimit)
{
Listing = listing;
CurrentPageIndex = -1;
CurrentPage = new Thing[0];
// Set the listings per page (if not specified, use the Reddit default of 25) and the maximum listings
LimitPerRequest = (limitPerRequest <= 0 ? DefaultListingPerRequest : limitPerRequest);
MaximumLimit = maximumLimit;
}
public T Current
{
get
{
return (T)CurrentPage[CurrentPageIndex];
}
}
private void FetchNextPage()
{
var url = Listing.Url;
if (After != null)
{
url += (url.Contains("?") ? "&" : "?") + "after=" + After;
}
if (LimitPerRequest != -1)
{
int limit = LimitPerRequest;
if (limit > MaximumLimit)
{
// If the limit is more than the maximum number of listings, adjust
limit = MaximumLimit;
}
else if (Count + limit > MaximumLimit)
{
// If a smaller subset of listings are needed, adjust the limit
limit = MaximumLimit - Count;
}
if (limit > 0)
{
// Add the limit, the maximum number of items to be returned per page
url += (url.Contains("?") ? "&" : "?") + "limit=" + limit;
}
}
if (Count > 0)
{
// Add the count, the number of items already seen in this listing
// The Reddit API uses this to determine when to give values for before and after fields
url += (url.Contains("?") ? "&" : "?") + "count=" + Count;
}
var request = Listing.WebAgent.CreateGet(url);
var response = request.GetResponse();
var data = Listing.WebAgent.GetResponseString(response.GetResponseStream());
var json = JToken.Parse(data);
if (json["kind"].ValueOrDefault() != "Listing")
throw new FormatException("Reddit responded with an object that is not a listing.");
Parse(json);
}
private void Parse(JToken json)
{
var children = json["data"]["children"] as JArray;
CurrentPage = new Thing[children.Count];
for (int i = 0; i < CurrentPage.Length; i++)
CurrentPage[i] = Thing.Parse(Listing.Reddit, children[i], Listing.WebAgent);
// Increase the total count of items returned
Count += CurrentPage.Length;
After = json["data"]["after"].Value();
Before = json["data"]["before"].Value();
}
public void Dispose()
{
// ...
}
object System.Collections.IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
CurrentPageIndex++;
if (CurrentPageIndex == CurrentPage.Length)
{
if (After == null && CurrentPageIndex != 0)
{
// No more pages to return
return false;
}
if (MaximumLimit != -1 && Count >= MaximumLimit)
{
// Maximum listing count returned
return false;
}
// Get the next page
FetchNextPage();
CurrentPageIndex = 0;
if (CurrentPage.Length == 0)
{
// No listings were returned in the page
return false;
}
}
return true;
}
public void Reset()
{
After = Before = null;
CurrentPageIndex = -1;
CurrentPage = new Thing[0];
}
}
#pragma warning restore
}
}
================================================
FILE: RedditSharp/ModActionType.cs
================================================
using Newtonsoft.Json;
using System;
namespace RedditSharp
{
public enum ModActionType
{
BanUser,
UnBanUser,
RemoveLink,
ApproveLink,
RemoveComment,
ApproveComment,
AddModerator,
InviteModerator,
UnInviteModerator,
AcceptModeratorInvite,
RemoveModerator,
AddContributor,
RemoveContributor,
EditSettings,
EditFlair,
Distinguish,
MarkNSFW,
WikiBanned,
WikiContributor,
WikiUnBanned,
WikiPageListed,
RemoveWikiContributor,
WikiRevise,
WikiPermlevel,
IgnoreReports,
UnIgnoreReports,
SetPermissions,
SetSuggestedsort,
Sticky,
UnSticky,
SetContestMode,
UnSetContestMode,
LockPost, //actual value is "Lock" but it's a reserved word
Unlock,
MuteUser,
UnMuteUser
}
public class ModActionTypeConverter : JsonConverter
{
///
/// Replaces "LockPost" with "lock" since "lock" is a reserved word and can't be used in the enum
///
/// String representation of enum value recognized by Reddit's api
public static string GetRedditParamName(ModActionType action)
{
if (action == ModActionType.LockPost) return "lock";
else return action.ToString("g").ToLower();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ModActionType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string value = reader.Value.ToString();
if (value.ToLower() == "lock")
{
return ModActionType.LockPost;
}
else
{
return Enum.Parse(typeof(ModActionType), value, true);
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null) writer.WriteNull();
else writer.WriteValue(GetRedditParamName((ModActionType) value));
}
}
}
================================================
FILE: RedditSharp/ModeratorPermission.cs
================================================
using System;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace RedditSharp
{
[Flags]
public enum ModeratorPermission
{
None = 0x00,
Access = 0x01,
Config = 0x02,
Flair = 0x04,
Mail = 0x08,
Posts = 0x10,
Wiki = 0x20,
All = Access | Config | Flair | Mail | Posts | Wiki
}
internal class ModeratorPermissionConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var data = String.Join(",", JArray.Load(reader).Select(t => t.ToString()));
ModeratorPermission result;
var valid = Enum.TryParse(data, true, out result);
if (!valid)
result = ModeratorPermission.None;
return result;
}
public override bool CanConvert(Type objectType)
{
// NOTE: Not sure if this is what is supposed to be returned
// This method wasn't called in my (Sharparam) tests so unsure what it does
return objectType == typeof (ModeratorPermission);
}
}
}
================================================
FILE: RedditSharp/ModeratorUser.cs
================================================
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace RedditSharp
{
public class ModeratorUser
{
public ModeratorUser(Reddit reddit, JToken json)
{
JsonConvert.PopulateObject(json.ToString(), this, reddit.JsonSerializerSettings);
}
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("mod_permissions")]
[JsonConverter(typeof (ModeratorPermissionConverter))]
public ModeratorPermission Permissions { get; set; }
public override string ToString()
{
return Name;
}
}
}
================================================
FILE: RedditSharp/MultipartFormBuilder.cs
================================================
using System;
using System.IO;
using System.Net;
namespace RedditSharp
{
public class MultipartFormBuilder
{
public HttpWebRequest Request { get; set; }
private string Boundary { get; set; }
private MemoryStream Buffer { get; set; }
private TextWriter TextBuffer { get; set; }
public MultipartFormBuilder(HttpWebRequest request)
{
// TODO: See about regenerating the boundary when needed
Request = request;
var random = new Random();
Boundary = "----------" + CreateRandomBoundary();
request.ContentType = "multipart/form-data; boundary=" + Boundary;
Buffer = new MemoryStream();
TextBuffer = new StreamWriter(Buffer);
}
private string CreateRandomBoundary()
{
// TODO: There's probably a better way to go about this
const string characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
string value = "";
var random = new Random();
for (int i = 0; i < 10; i++)
value += characters[random.Next(characters.Length)];
return value;
}
public void AddDynamic(object data)
{
var type = data.GetType();
var properties = type.GetProperties();
foreach (var property in properties)
{
var entry = Convert.ToString(property.GetValue(data, null));
AddString(property.Name, entry);
}
}
public void AddString(string name, string value)
{
TextBuffer.Write("{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n",
"--" + Boundary, name, value);
TextBuffer.Flush();
}
public void AddFile(string name, string filename, byte[] value, string contentType)
{
TextBuffer.Write("{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n",
"--" + Boundary, name, filename, contentType);
TextBuffer.Flush();
Buffer.Write(value, 0, value.Length);
Buffer.Flush();
TextBuffer.Write("\r\n");
TextBuffer.Flush();
}
public void Finish()
{
TextBuffer.Write("--" + Boundary + "--");
TextBuffer.Flush();
var stream = Request.GetRequestStream();
Buffer.Seek(0, SeekOrigin.Begin);
Buffer.WriteTo(stream);
stream.Close();
}
}
}
================================================
FILE: RedditSharp/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("RedditSharp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RedditSharp")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("5b1e351d-35b7-443e-9341-52c069a14886")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
================================================
FILE: RedditSharp/RateLimitException.cs
================================================
using System;
namespace RedditSharp
{
public class RateLimitException : Exception
{
public TimeSpan TimeToReset { get; set; }
public RateLimitException(TimeSpan timeToReset)
{
TimeToReset = timeToReset;
}
}
}
================================================
FILE: RedditSharp/Reddit.cs
================================================
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Net;
using System.Security.Authentication;
using RedditSharp.Things;
using System.Threading.Tasks;
using DefaultWebAgent = RedditSharp.WebAgent;
namespace RedditSharp
{
///
/// Class to communicate with Reddit.com
///
public class Reddit
{
#region Constant Urls
private const string SslLoginUrl = "https://ssl.reddit.com/api/login";
private const string LoginUrl = "/api/login/username";
private const string UserInfoUrl = "/user/{0}/about.json";
private const string MeUrl = "/api/me.json";
private const string OAuthMeUrl = "/api/v1/me.json";
private const string SubredditAboutUrl = "/r/{0}/about.json";
private const string ComposeMessageUrl = "/api/compose";
private const string RegisterAccountUrl = "/api/register";
private const string GetThingUrl = "/api/info.json?id={0}";
private const string GetCommentUrl = "/r/{0}/comments/{1}/foo/{2}";
private const string GetPostUrl = "{0}.json";
private const string DomainUrl = "www.reddit.com";
private const string OAuthDomainUrl = "oauth.reddit.com";
private const string SearchUrl = "/search.json?q={0}&restrict_sr=off&sort={1}&t={2}";
private const string UrlSearchPattern = "url:'{0}'";
private const string NewSubredditsUrl = "/subreddits/new.json";
private const string PopularSubredditsUrl = "/subreddits/popular.json";
private const string GoldSubredditsUrl = "/subreddits/gold.json";
private const string DefaultSubredditsUrl = "/subreddits/default.json";
private const string SearchSubredditsUrl = "/subreddits/search.json?q={0}";
#endregion
#region Static Variables
static Reddit()
{
DefaultWebAgent.UserAgent = "";
DefaultWebAgent.RateLimit = DefaultWebAgent.RateLimitMode.Pace;
DefaultWebAgent.Protocol = "https";
DefaultWebAgent.RootDomain = "www.reddit.com";
}
#endregion
internal IWebAgent WebAgent { get; set; }
///
/// Captcha solver instance to use when solving captchas.
///
public ICaptchaSolver CaptchaSolver;
///
/// The authenticated user for this instance.
///
public AuthenticatedUser User { get; set; }
///
/// Sets the Rate Limiting Mode of the underlying WebAgent
///
public DefaultWebAgent.RateLimitMode RateLimit
{
get { return DefaultWebAgent.RateLimit; }
set { DefaultWebAgent.RateLimit = value; }
}
internal JsonSerializerSettings JsonSerializerSettings { get; set; }
///
/// Gets the FrontPage using the current Reddit instance.
///
public Subreddit FrontPage
{
get { return Subreddit.GetFrontPage(this); }
}
///
/// Gets /r/All using the current Reddit instance.
///
public Subreddit RSlashAll
{
get { return Subreddit.GetRSlashAll(this); }
}
public Reddit()
: this(true) { }
public Reddit(bool useSsl)
{
DefaultWebAgent defaultAgent = new DefaultWebAgent();
JsonSerializerSettings = new JsonSerializerSettings
{
CheckAdditionalContent = false,
DefaultValueHandling = DefaultValueHandling.Ignore
};
DefaultWebAgent.Protocol = useSsl ? "https" : "http";
WebAgent = defaultAgent;
CaptchaSolver = new ConsoleCaptchaSolver();
}
public Reddit(DefaultWebAgent.RateLimitMode limitMode, bool useSsl = true)
: this(useSsl)
{
DefaultWebAgent.UserAgent = "";
DefaultWebAgent.RateLimit = limitMode;
DefaultWebAgent.RootDomain = "www.reddit.com";
}
public Reddit(string username, string password, bool useSsl = true)
: this(useSsl)
{
LogIn(username, password, useSsl);
}
public Reddit(string accessToken)
: this(true)
{
DefaultWebAgent.RootDomain = OAuthDomainUrl;
WebAgent.AccessToken = accessToken;
InitOrUpdateUser();
}
///
/// Creates a Reddit instance with the given WebAgent implementation
///
/// Implementation of IWebAgent interface. Used to generate requests.
public Reddit(IWebAgent agent)
{
WebAgent = agent;
JsonSerializerSettings = new JsonSerializerSettings
{
CheckAdditionalContent = false,
DefaultValueHandling = DefaultValueHandling.Ignore
};
CaptchaSolver = new ConsoleCaptchaSolver();
}
///
/// Creates a Reddit instance with the given WebAgent implementation
///
/// Implementation of IWebAgent interface. Used to generate requests.
/// Whether to run InitOrUpdateUser, requires to have credentials first.
public Reddit(IWebAgent agent, bool initUser)
{
WebAgent = agent;
JsonSerializerSettings = new JsonSerializerSettings
{
CheckAdditionalContent = false,
DefaultValueHandling = DefaultValueHandling.Ignore
};
CaptchaSolver = new ConsoleCaptchaSolver();
if(initUser) InitOrUpdateUser();
}
///
/// Logs in the current Reddit instance.
///
/// The username of the user to log on to.
/// The password of the user to log on to.
/// Whether to use SSL or not. (default: true)
///
public AuthenticatedUser LogIn(string username, string password, bool useSsl = true)
{
if (Type.GetType("Mono.Runtime") != null)
ServicePointManager.ServerCertificateValidationCallback = (s, c, ch, ssl) => true;
WebAgent.Cookies = new CookieContainer();
HttpWebRequest request;
if (useSsl)
request = WebAgent.CreatePost(SslLoginUrl);
else
request = WebAgent.CreatePost(LoginUrl);
var stream = request.GetRequestStream();
if (useSsl)
{
WebAgent.WritePostBody(stream, new
{
user = username,
passwd = password,
api_type = "json"
});
}
else
{
WebAgent.WritePostBody(stream, new
{
user = username,
passwd = password,
api_type = "json",
op = "login"
});
}
stream.Close();
var response = (HttpWebResponse)request.GetResponse();
var result = WebAgent.GetResponseString(response.GetResponseStream());
var json = JObject.Parse(result)["json"];
if (json["errors"].Count() != 0)
throw new AuthenticationException("Incorrect login.");
InitOrUpdateUser();
return User;
}
public RedditUser GetUser(string name)
{
var request = WebAgent.CreateGet(string.Format(UserInfoUrl, name));
var response = request.GetResponse();
var result = WebAgent.GetResponseString(response.GetResponseStream());
var json = JObject.Parse(result);
return new RedditUser().Init(this, json, WebAgent);
}
///
/// Initializes the User property if it's null,
/// otherwise replaces the existing user object
/// with a new one fetched from reddit servers.
///
public void InitOrUpdateUser()
{
var request = WebAgent.CreateGet(string.IsNullOrEmpty(WebAgent.AccessToken) ? MeUrl : OAuthMeUrl);
var response = (HttpWebResponse)request.GetResponse();
var result = WebAgent.GetResponseString(response.GetResponseStream());
var json = JObject.Parse(result);
User = new AuthenticatedUser().Init(this, json, WebAgent);
}
#region Obsolete Getter Methods
[Obsolete("Use User property instead")]
public AuthenticatedUser GetMe()
{
return User;
}
#endregion Obsolete Getter Methods
public Subreddit GetSubreddit(string name)
{
if (name.StartsWith("r/"))
name = name.Substring(2);
if (name.StartsWith("/r/"))
name = name.Substring(3);
name = name.TrimEnd('/');
return GetThing(string.Format(SubredditAboutUrl, name));
}
///
/// Returns the subreddit.
///
/// The name of the subreddit
/// The Subreddit by given name
public async Task GetSubredditAsync(string name)
{
if (name.StartsWith("r/"))
name = name.Substring(2);
if (name.StartsWith("/r/"))
name = name.Substring(3);
name = name.TrimEnd('/');
return await GetThingAsync(string.Format(SubredditAboutUrl, name));
}
public Domain GetDomain(string domain)
{
if (!domain.StartsWith("http://") && !domain.StartsWith("https://"))
domain = "http://" + domain;
var uri = new Uri(domain);
return new Domain(this, uri, WebAgent);
}
public JToken GetToken(Uri uri)
{
var url = uri.AbsoluteUri;
if (url.EndsWith("/"))
url = url.Remove(url.Length - 1);
var request = WebAgent.CreateGet(string.Format(GetPostUrl, url));
var response = request.GetResponse();
var data = WebAgent.GetResponseString(response.GetResponseStream());
var json = JToken.Parse(data);
return json[0]["data"]["children"].First;
}
public Post GetPost(Uri uri)
{
return new Post().Init(this, GetToken(uri), WebAgent);
}
///
///
///
///
///
///
/// The subreddit to send the message as (optional).
///
///
/// If is passed in then the message is sent from the subreddit. the sender must be a mod of the specified subreddit.
/// Thrown when a subreddit is passed in and the user is not a mod of that sub.
public void ComposePrivateMessage(string subject, string body, string to, string fromSubReddit = "", string captchaId = "", string captchaAnswer = "")
{
if (User == null)
throw new Exception("User can not be null.");
if (!String.IsNullOrWhiteSpace(fromSubReddit))
{
var subReddit = this.GetSubreddit(fromSubReddit);
var modNameList = subReddit.Moderators.Select(b => b.Name).ToList();
if (!modNameList.Contains(User.Name))
throw new AuthenticationException(
String.Format(
@"User {0} is not a moderator of subreddit {1}.",
User.Name,
subReddit.Name));
}
var request = WebAgent.CreatePost(ComposeMessageUrl);
WebAgent.WritePostBody(request.GetRequestStream(), new
{
api_type = "json",
subject,
text = body,
to,
from_sr = fromSubReddit,
uh = User.Modhash,
iden = captchaId,
captcha = captchaAnswer
});
var response = request.GetResponse();
var result = WebAgent.GetResponseString(response.GetResponseStream());
var json = JObject.Parse(result);
ICaptchaSolver solver = CaptchaSolver; // Prevent race condition
if (json["json"]["errors"].Any() && json["json"]["errors"][0][0].ToString() == "BAD_CAPTCHA" && solver != null)
{
captchaId = json["json"]["captcha"].ToString();
CaptchaResponse captchaResponse = solver.HandleCaptcha(new Captcha(captchaId));
if (!captchaResponse.Cancel) // Keep trying until we are told to cancel
ComposePrivateMessage(subject, body, to, captchaId, captchaResponse.Answer);
}
}
///
/// Registers a new Reddit user
///
/// The username for the new account.
/// The password for the new account.
/// The optional recovery email for the new account.
/// The newly created user account
public AuthenticatedUser RegisterAccount(string userName, string passwd, string email = "")
{
var request = WebAgent.CreatePost(RegisterAccountUrl);
WebAgent.WritePostBody(request.GetRequestStream(), new
{
api_type = "json",
email = email,
passwd = passwd,
passwd2 = passwd,
user = userName
});
var response = request.GetResponse();
var result = WebAgent.GetResponseString(response.GetResponseStream());
var json = JObject.Parse(result);
return new AuthenticatedUser().Init(this, json, WebAgent);
// TODO: Error
}
public Thing GetThingByFullname(string fullname)
{
var request = WebAgent.CreateGet(string.Format(GetThingUrl, fullname));
var response = request.GetResponse();
var data = WebAgent.GetResponseString(response.GetResponseStream());
var json = JToken.Parse(data);
return Thing.Parse(this, json["data"]["children"][0], WebAgent);
}
public Comment GetComment(string subreddit, string name, string linkName)
{
try
{
if (linkName.StartsWith("t3_"))
linkName = linkName.Substring(3);
if (name.StartsWith("t1_"))
name = name.Substring(3);
var url = string.Format(GetCommentUrl, subreddit, linkName, name);
return GetComment(new Uri(url));
}
catch (WebException)
{
return null;
}
}
public Comment GetComment(Uri uri)
{
var url = string.Format(GetPostUrl, uri.AbsoluteUri);
var request = WebAgent.CreateGet(url);
var response = request.GetResponse();
var data = WebAgent.GetResponseString(response.GetResponseStream());
var json = JToken.Parse(data);
var sender = new Post().Init(this, json[0]["data"]["children"][0], WebAgent);
return new Comment().Init(this, json[1]["data"]["children"][0], WebAgent, sender);
}
public Listing SearchByUrl(string url) where T : Thing
{
var urlSearchQuery = string.Format(UrlSearchPattern, url);
return Search(urlSearchQuery);
}
public Listing Search(string query, Sorting sortE = Sorting.Relevance, TimeSorting timeE = TimeSorting.All) where T : Thing
{
string sort = sortE.ToString().ToLower();
string time = timeE.ToString().ToLower();
return new Listing(this, string.Format(SearchUrl, query, sort, time), WebAgent);
}
public Listing SearchByTimestamp(DateTime from, DateTime to, string query = "", string subreddit = "", Sorting sortE = Sorting.Relevance, TimeSorting timeE = TimeSorting.All) where T : Thing
{
string sort = sortE.ToString().ToLower();
string time = timeE.ToString().ToLower();
var fromUnix = (from - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
var toUnix = (to - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
string searchQuery = "(and+timestamp:" + fromUnix + ".." + toUnix + "+'" + query + "'+" + "subreddit:'" + subreddit + "')&syntax=cloudsearch";
return new Listing(this, string.Format(SearchUrl, searchQuery, sort, time), WebAgent);
}
#region SubredditSearching
///
/// Returns a Listing of newly created subreddits.
///
///
public Listing GetNewSubreddits()
{
return new Listing(this, NewSubredditsUrl, WebAgent);
}
///
/// Returns a Listing of the most popular subreddits.
///
///
public Listing GetPopularSubreddits()
{
return new Listing(this, PopularSubredditsUrl, WebAgent);
}
///
/// Returns a Listing of Gold-only subreddits. This endpoint will not return anything if the authenticated Reddit account does not currently have gold.
///
///
public Listing GetGoldSubreddits()
{
return new Listing(this, GoldSubredditsUrl, WebAgent);
}
///
/// Returns the Listing of default subreddits.
///
///
public Listing GetDefaultSubreddits()
{
return new Listing(this, DefaultSubredditsUrl, WebAgent);
}
///
/// Returns the Listing of subreddits related to a query.
///
///
public Listing SearchSubreddits(string query)
{
return new Listing(this, string.Format(SearchSubredditsUrl, query), WebAgent);
}
#endregion SubredditSearching
#region Helpers
protected async internal Task GetThingAsync(string url) where T : Thing
{
var request = WebAgent.CreateGet(url);
var response = request.GetResponse();
var data = WebAgent.GetResponseString(response.GetResponseStream());
var json = JToken.Parse(data);
var ret = await Thing.ParseAsync(this, json, WebAgent);
return (T)ret;
}
protected internal T GetThing(string url) where T : Thing
{
var request = WebAgent.CreateGet(url);
var response = request.GetResponse();
var data = WebAgent.GetResponseString(response.GetResponseStream());
var json = JToken.Parse(data);
return (T)Thing.Parse(this, json, WebAgent);
}
#endregion
}
}
================================================
FILE: RedditSharp/RedditAPINameAttribute.cs
================================================
using System;
namespace RedditSharp
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
internal class RedditAPINameAttribute : Attribute
{
internal string Name { get; private set; }
internal RedditAPINameAttribute(string name)
{
Name = name;
}
public override string ToString()
{
return Name;
}
}
}
================================================
FILE: RedditSharp/RedditException.cs
================================================
using System;
using System.Runtime.Serialization;
namespace RedditSharp
{
///
/// Represents an error that occurred during accessing or manipulating data on Reddit.
///
[Serializable]
public class RedditException : Exception
{
///
/// Initializes a new instance of the RedditException class.
///
public RedditException()
{
}
///
/// Initializes a new instance of the RedditException class with a specified error message.
///
/// The message that describes the error.
public RedditException(string message)
: base(message)
{
}
///
/// Initializes a new instance of the RedditException class with a specified error message and
/// a referenced inner exception that is the cause of this exception.
///
/// The message that describes the error.
/// The exception that is the cause of the current exception, or a null
/// reference (Nothing in Visual Basic) if no inner exception is specified.
public RedditException(string message, Exception inner)
: base(message, inner)
{
}
///
/// Initializes a new instance of the RedditException class with serialized data.
///
/// The System.Runtime.Serialization.SerializationInfo that holds the
/// serialized object data about the exception being thrown.
/// The System.Runtime.Serialization.StreamingContext that contains
/// contextual information about the source or destination.
/// The info parameter is null.
/// The class name
/// is null or System.Exception.HResult is zero (0).
protected RedditException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}
================================================
FILE: RedditSharp/RedditSharp.csproj
================================================
Debug
AnyCPU
8.0.30703
2.0
{A368CB75-75F0-4489-904D-B5CEBB0FE624}
Library
Properties
RedditSharp
RedditSharp
512
v4.5
True
full
False
bin\Debug\
DEBUG;TRACE
prompt
4
false
pdbonly
True
bin\Release\
TRACE
prompt
4
false
..\HtmlAgilityPack.dll
..\Newtonsoft.Json.dll
================================================
FILE: RedditSharp/SpamFilterSettings.cs
================================================
namespace RedditSharp
{
public class SpamFilterSettings
{
public SpamFilterStrength LinkPostStrength { get; set; }
public SpamFilterStrength SelfPostStrength { get; set; }
public SpamFilterStrength CommentStrength { get; set; }
public SpamFilterSettings()
{
LinkPostStrength = SpamFilterStrength.High;
SelfPostStrength = SpamFilterStrength.High;
CommentStrength = SpamFilterStrength.High;
}
}
}
================================================
FILE: RedditSharp/SubmitData.cs
================================================
namespace RedditSharp
{
internal abstract class SubmitData
{
[RedditAPIName("api_type")]
internal string APIType { get; set; }
[RedditAPIName("kind")]
internal string Kind { get; set; }
[RedditAPIName("sr")]
internal string Subreddit { get; set; }
[RedditAPIName("uh")]
internal string UserHash { get; set; }
[RedditAPIName("title")]
internal string Title { get; set; }
[RedditAPIName("iden")]
internal string Iden { get; set; }
[RedditAPIName("captcha")]
internal string Captcha { get; set; }
[RedditAPIName("resubmit")]
internal bool Resubmit { get; set; }
protected SubmitData()
{
APIType = "json";
}
}
}
================================================
FILE: RedditSharp/SubredditImage.cs
================================================
using System;
namespace RedditSharp
{
public class SubredditImage
{
private const string DeleteImageUrl = "/api/delete_sr_img";
private Reddit Reddit { get; set; }
private IWebAgent WebAgent { get; set; }
public SubredditImage(Reddit reddit, SubredditStyle subredditStyle,
string cssLink, string name, IWebAgent webAgent)
{
Reddit = reddit;
WebAgent = webAgent;
SubredditStyle = subredditStyle;
Name = name;
CssLink = cssLink;
}
public SubredditImage(Reddit reddit, SubredditStyle subreddit,
string cssLink, string name, string url, IWebAgent webAgent)
: this(reddit, subreddit, cssLink, name, webAgent)
{
Url = new Uri(url);
// Handle legacy image urls
// http://thumbs.reddit.com/FULLNAME_NUMBER.png
int discarded;
if (int.TryParse(url, out discarded))
Url = new Uri(string.Format("http://thumbs.reddit.com/{0}_{1}.png", subreddit.Subreddit.FullName, url), UriKind.Absolute);
}
public string CssLink { get; set; }
public string Name { get; set; }
public Uri Url { get; set; }
public SubredditStyle SubredditStyle { get; set; }
public void Delete()
{
var request = WebAgent.CreatePost(DeleteImageUrl);
var stream = request.GetRequestStream();
WebAgent.WritePostBody(stream, new
{
img_name = Name,
uh = Reddit.User.Modhash,
r = SubredditStyle.Subreddit.Name
});
stream.Close();
var response = request.GetResponse();
var data = WebAgent.GetResponseString(response.GetResponseStream());
SubredditStyle.Images.Remove(this);
}
}
}
================================================
FILE: RedditSharp/SubredditSettings.cs
================================================
using System.Web;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RedditSharp.Things;
namespace RedditSharp
{
public class SubredditSettings
{
private const string SiteAdminUrl = "/api/site_admin";
private const string DeleteHeaderImageUrl = "/api/delete_sr_header";
private Reddit Reddit { get; set; }
private IWebAgent WebAgent { get; set; }
[JsonIgnore]
public Subreddit Subreddit { get; set; }
public SubredditSettings(Reddit reddit, Subreddit subreddit, IWebAgent webAgent)
{
Subreddit = subreddit;
Reddit = reddit;
WebAgent = webAgent;
// Default settings, for use when reduced information is given
AllowAsDefault = true;
Domain = null;
Sidebar = string.Empty;
Language = "en";
Title = Subreddit.DisplayName;
WikiEditKarma = 100;
WikiEditAge = 10;
UseDomainCss = false;
UseDomainSidebar = false;
HeaderHoverText = string.Empty;
NSFW = false;
PublicDescription = string.Empty;
WikiEditMode = WikiEditMode.None;
SubredditType = SubredditType.Public;
ShowThumbnails = true;
ContentOptions = ContentOptions.All;
SpamFilter = new SpamFilterSettings();
}
public SubredditSettings(Subreddit subreddit, Reddit reddit, JObject json, IWebAgent webAgent) : this(reddit, subreddit, webAgent)
{
var data = json["data"];
AllowAsDefault = data["default_set"].ValueOrDefault();
Domain = data["domain"].ValueOrDefault();
Sidebar = HttpUtility.HtmlDecode(data["description"].ValueOrDefault() ?? string.Empty);
Language = data["language"].ValueOrDefault();
Title = data["title"].ValueOrDefault();
WikiEditKarma = data["wiki_edit_karma"].ValueOrDefault();
UseDomainCss = data["domain_css"].ValueOrDefault();
UseDomainSidebar = data["domain_sidebar"].ValueOrDefault();
HeaderHoverText = data["header_hover_text"].ValueOrDefault();
NSFW = data["over_18"].ValueOrDefault();
PublicDescription = HttpUtility.HtmlDecode(data["public_description"].ValueOrDefault() ?? string.Empty);
SpamFilter = new SpamFilterSettings
{
LinkPostStrength = GetSpamFilterStrength(data["spam_links"].ValueOrDefault()),
SelfPostStrength = GetSpamFilterStrength(data["spam_selfposts"].ValueOrDefault()),
CommentStrength = GetSpamFilterStrength(data["spam_comments"].ValueOrDefault())
};
if (data["wikimode"] != null)
{
var wikiMode = data["wikimode"].ValueOrDefault();
switch (wikiMode)
{
case "disabled":
WikiEditMode = WikiEditMode.None;
break;
case "modonly":
WikiEditMode = WikiEditMode.Moderators;
break;
case "anyone":
WikiEditMode = WikiEditMode.All;
break;
}
}
if (data["subreddit_type"] != null)
{
var type = data["subreddit_type"].ValueOrDefault();
switch (type)
{
case "public":
SubredditType = SubredditType.Public;
break;
case "private":
SubredditType = SubredditType.Private;
break;
case "restricted":
SubredditType = SubredditType.Restricted;
break;
}
}
ShowThumbnails = data["show_media"].ValueOrDefault();
WikiEditAge = data["wiki_edit_age"].ValueOrDefault();
if (data["content_options"] != null)
{
var contentOptions = data["content_options"].ValueOrDefault();
switch (contentOptions)
{
case "any":
ContentOptions = ContentOptions.All;
break;
case "link":
ContentOptions = ContentOptions.LinkOnly;
break;
case "self":
ContentOptions = ContentOptions.SelfOnly;
break;
}
}
}
public bool AllowAsDefault { get; set; }
public string Domain { get; set; }
public string Sidebar { get; set; }
public string Language { get; set; }
public string Title { get; set; }
public int WikiEditKarma { get; set; }
public bool UseDomainCss { get; set; }
public bool UseDomainSidebar { get; set; }
public string HeaderHoverText { get; set; }
public bool NSFW { get; set; }
public string PublicDescription { get; set; }
public WikiEditMode WikiEditMode { get; set; }
public SubredditType SubredditType { get; set; }
public bool ShowThumbnails { get; set; }
public int WikiEditAge { get; set; }
public ContentOptions ContentOptions { get; set; }
public SpamFilterSettings SpamFilter { get; set; }
public void UpdateSettings()
{
var request = WebAgent.CreatePost(SiteAdminUrl);
var stream = request.GetRequestStream();
string link_type;
string type;
string wikimode;
switch (ContentOptions)
{
case ContentOptions.All:
link_type = "any";
break;
case ContentOptions.LinkOnly:
link_type = "link";
break;
default:
link_type = "self";
break;
}
switch (SubredditType)
{
case SubredditType.Public:
type = "public";
break;
case SubredditType.Private:
type = "private";
break;
default:
type = "restricted";
break;
}
switch (WikiEditMode)
{
case WikiEditMode.All:
wikimode = "anyone";
break;
case WikiEditMode.Moderators:
wikimode = "modonly";
break;
default:
wikimode = "disabled";
break;
}
WebAgent.WritePostBody(stream, new
{
allow_top = AllowAsDefault,
description = Sidebar,
domain = Domain,
lang = Language,
link_type,
over_18 = NSFW,
public_description = PublicDescription,
show_media = ShowThumbnails,
sr = Subreddit.FullName,
title = Title,
type,
uh = Reddit.User.Modhash,
wiki_edit_age = WikiEditAge,
wiki_edit_karma = WikiEditKarma,
wikimode,
spam_links = SpamFilter == null ? null : SpamFilter.LinkPostStrength.ToString().ToLowerInvariant(),
spam_selfposts = SpamFilter == null ? null : SpamFilter.SelfPostStrength.ToString().ToLowerInvariant(),
spam_comments = SpamFilter == null ? null : SpamFilter.CommentStrength.ToString().ToLowerInvariant(),
api_type = "json"
}, "header-title", HeaderHoverText);
stream.Close();
var response = request.GetResponse();
var data = WebAgent.GetResponseString(response.GetResponseStream());
}
///
/// Resets the subreddit's header image to the Reddit logo
///
public void ResetHeaderImage()
{
var request = WebAgent.CreatePost(DeleteHeaderImageUrl);
var stream = request.GetRequestStream();
WebAgent.WritePostBody(stream, new
{
uh = Reddit.User.Modhash,
r = Subreddit.Name
});
stream.Close();
var response = request.GetResponse();
var data = WebAgent.GetResponseString(response.GetResponseStream());
}
private SpamFilterStrength GetSpamFilterStrength(string rawValue)
{
switch(rawValue)
{
case "low":
return SpamFilterStrength.Low;
case "high":
return SpamFilterStrength.High;
case "all":
return SpamFilterStrength.All;
default:
return SpamFilterStrength.High;
}
}
}
public enum WikiEditMode
{
None,
Moderators,
All
}
public enum SubredditType
{
Public,
Restricted,
Private
}
public enum ContentOptions
{
All,
LinkOnly,
SelfOnly
}
public enum SpamFilterStrength
{
Low,
High,
All
}
}
================================================
FILE: RedditSharp/SubredditStyle.cs
================================================
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using System.Web;
using RedditSharp.Things;
namespace RedditSharp
{
public class SubredditStyle
{
private const string UploadImageUrl = "/api/upload_sr_img";
private const string UpdateCssUrl = "/api/subreddit_stylesheet";
private Reddit Reddit { get; set; }
private IWebAgent WebAgent { get; set; }
public SubredditStyle(Reddit reddit, Subreddit subreddit, IWebAgent webAgent)
{
Reddit = reddit;
Subreddit = subreddit;
WebAgent = webAgent;
}
public SubredditStyle(Reddit reddit, Subreddit subreddit, JToken json, IWebAgent webAgent) : this(reddit, subreddit, webAgent)
{
Images = new List();
var data = json["data"];
CSS = HttpUtility.HtmlDecode(data["stylesheet"].Value());
foreach (var image in data["images"])
{
Images.Add(new SubredditImage(
Reddit, this, image["link"].Value(),
image["name"].Value(), image["url"].Value(), WebAgent));
}
}
public string CSS { get; set; }
public List Images { get; set; }
public Subreddit Subreddit { get; set; }
public void UpdateCss()
{
var request = WebAgent.CreatePost(UpdateCssUrl);
var stream = request.GetRequestStream();
WebAgent.WritePostBody(stream, new
{
op = "save",
stylesheet_contents = CSS,
uh = Reddit.User.Modhash,
api_type = "json",
r = Subreddit.Name
});
stream.Close();
var response = request.GetResponse();
var data = WebAgent.GetResponseString(response.GetResponseStream());
var json = JToken.Parse(data);
}
public void UploadImage(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 = Subreddit.Name,
formid = "image-upload",
img_type = imageType == ImageType.PNG ? "png" : "jpg",
upload = ""
});
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 enum ImageType
{
PNG,
JPEG
}
}
================================================
FILE: RedditSharp/TBUserNote.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RedditSharp
{
public class TBUserNote
{
public int NoteTypeIndex { get; set; }
public string NoteType { get; set; }
public string SubName { get; set; }
public string Submitter { get; set; }
public int SubmitterIndex { get; set; }
public string Message { get; set; }
public string AppliesToUsername { get; set; }
public string Url { get; set; }
private DateTime _timestamp;
public DateTime Timestamp
{
get { return _timestamp; }
set
{
_timestamp = DateTime.SpecifyKind(value, DateTimeKind.Utc);
}
}
}
}
================================================
FILE: RedditSharp/TextData.cs
================================================
namespace RedditSharp
{
internal class TextData : SubmitData
{
[RedditAPIName("text")]
internal string Text { get; set; }
internal TextData()
{
Kind = "self";
}
}
}
================================================
FILE: RedditSharp/Things/AuthenticatedUser.cs
================================================
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 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 ModeratorSubreddits
{
get
{
return new Listing(Reddit, ModeratorUrl, WebAgent);
}
}
public Listing UnreadMessages
{
get
{
return new Listing(Reddit, UnreadMessagesUrl, WebAgent);
}
}
public Listing ModerationQueue
{
get
{
return new Listing(Reddit, ModQueueUrl, WebAgent);
}
}
public Listing UnmoderatedLinks
{
get
{
return new Listing(Reddit, UnmoderatedUrl, WebAgent);
}
}
public Listing ModMail
{
get
{
return new Listing(Reddit, ModMailUrl, WebAgent);
}
}
public Listing PrivateMessages
{
get
{
return new Listing(Reddit, MessagesUrl, WebAgent);
}
}
public Listing Inbox
{
get
{
return new Listing(Reddit, InboxUrl, WebAgent);
}
}
public Listing Sent
{
get
{
return new Listing(Reddit, SentUrl, WebAgent);
}
}
#region Obsolete Getter Methods
[Obsolete("Use ModeratorSubreddits property instead")]
public Listing GetModeratorReddits()
{
return ModeratorSubreddits;
}
[Obsolete("Use UnreadMessages property instead")]
public Listing GetUnreadMessages()
{
return UnreadMessages;
}
[Obsolete("Use ModerationQueue property instead")]
public Listing GetModerationQueue()
{
return new Listing(Reddit, ModQueueUrl, WebAgent);
}
public Listing GetUnmoderatedLinks()
{
return new Listing(Reddit, UnmoderatedUrl, WebAgent);
}
[Obsolete("Use ModMail property instead")]
public Listing GetModMail()
{
return new Listing(Reddit, ModMailUrl, WebAgent);
}
[Obsolete("Use PrivateMessages property instead")]
public Listing GetPrivateMessages()
{
return new Listing(Reddit, MessagesUrl, WebAgent);
}
[Obsolete("Use Inbox property instead")]
public Listing GetInbox()
{
return new Listing(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; }
}
}
================================================
FILE: RedditSharp/Things/Comment.cs
================================================
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 EditUserTextUrl = "/api/editusertext";
private const string RemoveUrl = "/api/remove";
private const string DelUrl = "/api/del";
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 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();
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();
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();
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; }
[JsonIgnore]
public IList 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()));
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;
}
}
///
/// Replaces the text in this comment with the input text.
///
/// The text to replace the comment's contents
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.");
}
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;
}
public void Del()
{
var data = SimpleAction(DelUrl);
}
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());
}
}
}
================================================
FILE: RedditSharp/Things/Contributor.cs
================================================
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace RedditSharp.Things
{
public class Contributor : Thing
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("date")]
[JsonConverter(typeof(UnixTimestampConverter))]
public DateTime DateAdded { get; set; }
public Contributor Init(Reddit reddit, JToken json, IWebAgent webAgent)
{
CommonInit(json);
JsonConvert.PopulateObject(json.ToString(), this, reddit.JsonSerializerSettings);
return this;
}
private void CommonInit(JToken json)
{
base.Init(json);
}
}
}
================================================
FILE: RedditSharp/Things/CreatedThing.cs
================================================
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 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; }
}
}
================================================
FILE: RedditSharp/Things/ModAction.cs
================================================
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RedditSharp.Things
{
public class ModAction : Thing
{
[JsonProperty("action")]
[JsonConverter(typeof(ModActionTypeConverter))]
public ModActionType Action { get; set; }
[JsonProperty("created_utc")]
[JsonConverter(typeof(UnixTimestampConverter))]
public DateTime? TimeStamp { get; set; }
[JsonProperty("details")]
public string Details { get; set; }
[JsonProperty("mod")]
public string ModeratorName { get; set; }
[JsonProperty("target_author")]
public string TargetAuthorName { get; set; }
[JsonProperty("target_fullname")]
public string TargetThingFullname { get; set; }
[JsonProperty("target_permalink")]
public string TargetThingPermalink { get; set; }
[JsonIgnore]
public RedditUser TargetAuthor
{
get
{
return Reddit.GetUser(TargetAuthorName);
}
}
[JsonIgnore]
public Thing TargetThing
{
get
{
return Reddit.GetThingByFullname(TargetThingFullname);
}
}
[JsonIgnore]
private Reddit Reddit { get; set; }
[JsonIgnore]
private IWebAgent WebAgent { get; set; }
public ModAction Init(Reddit reddit, JToken json, IWebAgent webAgent)
{
CommonInit(reddit, json, webAgent);
JsonConvert.PopulateObject(json["data"].ToString(), this, reddit.JsonSerializerSettings);
return this;
}
public async Task 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 json, IWebAgent webAgent)
{
base.Init(json);
Reddit = reddit;
WebAgent = webAgent;
}
}
}
================================================
FILE: RedditSharp/Things/Post.cs
================================================
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 = "/r/{0}/api/flair";
private const string MarkNSFWUrl = "/api/marknsfw";
private const string UnmarkNSFWUrl = "/api/unmarknsfw";
private const string ContestModeUrl = "/api/set_contest_mode";
private const string StickyModeUrl = "/api/set_subreddit_sticky";
[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 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("thumbnail")]
[JsonConverter(typeof(UrlParser))]
public Uri Thumbnail { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("subreddit")]
public string SubredditName { get; set; }
[JsonIgnore]
public Subreddit Subreddit
{
get
{
return Reddit.GetSubreddit("/r/" + SubredditName);
}
}
[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()));
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, bool requiresModAction = false)
{
if (Reddit.User == null)
throw new AuthenticationException("No user logged in.");
var modNameList = this.Subreddit.Moderators.Select(b => b.Name).ToList();
if (requiresModAction && !modNameList.Contains(Reddit.User.Name))
throw new AuthenticationException(
String.Format(
@"User {0} is not a moderator of subreddit {1}.",
Reddit.User.Name,
this.Subreddit.Name));
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(DelUrl);
}
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);
}
public void StickyMode(bool state)
{
var data = SimpleActionToggle(StickyModeUrl, state, true);
}
#region Obsolete Getter Methods
[Obsolete("Use Comments property instead")]
public Comment[] GetComments()
{
return Comments;
}
#endregion Obsolete Getter Methods
///
/// Replaces the text in this post with the input text.
///
/// The text to replace the post's contents
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(string.Format(SetFlairUrl,SubredditName));
WebAgent.WritePostBody(request.GetRequestStream(), new
{
api_type = "json",
css_class = flairClass,
link = FullName,
name = Reddit.User.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 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();
foreach (var comment in postJson)
{
comments.Add(new Comment().Init(Reddit, comment, WebAgent, this));
}
return comments;
}
}
}
================================================
FILE: RedditSharp/Things/PrivateMessage.cs
================================================
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(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 Thread
{
get
{
if (string.IsNullOrEmpty(ParentID))
return null;
var id = ParentID.Remove(0, 3);
return new Listing(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 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();
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 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);
}
}
}
================================================
FILE: RedditSharp/Things/RedditUser.cs
================================================
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 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 Overview
{
get
{
return new Listing(Reddit, string.Format(OverviewUrl, Name), WebAgent);
}
}
public Listing LikedPosts
{
get
{
return new Listing(Reddit, string.Format(LikedUrl, Name), WebAgent);
}
}
public Listing DislikedPosts
{
get
{
return new Listing(Reddit, string.Format(DislikedUrl, Name), WebAgent);
}
}
public Listing Comments
{
get
{
return new Listing(Reddit, string.Format(CommentsUrl, Name), WebAgent);
}
}
public Listing Posts
{
get
{
return new Listing(Reddit, string.Format(LinksUrl, Name), WebAgent);
}
}
public Listing SubscribedSubreddits
{
get
{
return new Listing(Reddit, SubscribedSubredditsUrl, WebAgent);
}
}
///
/// Get a listing of comments and posts from the user sorted by , from time
/// and limited to .
///
/// How to sort the comments (hot, new, top, controversial).
/// How many comments to fetch per request. Max is 100.
/// What time frame of comments to show (hour, day, week, month, year, all).
/// The listing of comments requested.
public Listing GetOverview(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 overviewUrl = string.Format(OverviewUrl, Name);
overviewUrl += string.Format("?sort={0}&limit={1}&t={2}", Enum.GetName(typeof(Sort), sorting), limit, Enum.GetName(typeof(FromTime), fromTime));
return new Listing(Reddit, overviewUrl, WebAgent);
}
///
/// Get a listing of comments from the user sorted by , from time
/// and limited to .
///
/// How to sort the comments (hot, new, top, controversial).
/// How many comments to fetch per request. Max is 100.
/// What time frame of comments to show (hour, day, week, month, year, all).
/// The listing of comments requested.
public Listing 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(Reddit, commentsUrl, WebAgent);
}
///
/// Get a listing of posts from the user sorted by , from time
/// and limited to .
///
/// How to sort the posts (hot, new, top, controversial).
/// How many posts to fetch per request. Max is 100.
/// What time frame of posts to show (hour, day, week, month, year, all).
/// The listing of posts requested.
public Listing 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(Reddit, linksUrl, WebAgent);
}
public override string ToString()
{
return Name;
}
#region Obsolete Getter Methods
[Obsolete("Use Overview property instead")]
public Listing GetOverview()
{
return Overview;
}
[Obsolete("Use Comments property instead")]
public Listing GetComments()
{
return Comments;
}
[Obsolete("Use Posts property instead")]
public Listing GetPosts()
{
return Posts;
}
[Obsolete("Use SubscribedSubreddits property instead")]
public Listing GetSubscribedSubreddits()
{
return SubscribedSubreddits;
}
#endregion Obsolete Getter Methods
}
public enum Sort
{
New,
Hot,
Top,
Controversial
}
public enum FromTime
{
All,
Year,
Month,
Week,
Day,
Hour
}
}
================================================
FILE: RedditSharp/Things/Subreddit.cs
================================================
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;
using RedditSharp.Utils;
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 SubredditRisingUrl = "/r/{0}/rising.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}";
private const string SearchUrlDate = "/r/{0}/search.json?q=timestamp:{1}..{2}&restrict_sr=on&sort={3}&syntax=cloudsearch";
private const string ModLogUrl = "/r/{0}/about/log.json";
private const string ContributorsUrl = "/r/{0}/about/contributors.json";
[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("over_18")]
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; }
///
/// Property determining whether the current logged in user is a moderator on this subreddit.
///
[JsonProperty("user_is_moderator")]
public bool? UserIsModerator { get; set; }
///
/// Property giving the moderator permissions of the logged in user on this subreddit.
///
[JsonProperty("mod_permissions")]
[JsonConverter(typeof(ModeratorPermissionConverter))]
public ModeratorPermission ModPermissions { get; set; }
///
/// Property determining whether the current logged in user is banned from the subreddit.
///
[JsonProperty("user_is_banned")]
public bool? UserIsBanned { get; set; }
[JsonIgnore]
public string Name { get; set; }
public Listing GetTop(FromTime timePeriod)
{
if (Name == "/")
{
return new Listing(Reddit, "/top.json?t=" + Enum.GetName(typeof(FromTime), timePeriod).ToLower(), WebAgent);
}
return new Listing(Reddit, string.Format(SubredditTopUrl, Name, Enum.GetName(typeof(FromTime), timePeriod)).ToLower(), WebAgent);
}
public Listing Posts
{
get
{
if (Name == "/")
return new Listing(Reddit, "/.json", WebAgent);
return new Listing(Reddit, string.Format(SubredditPostUrl, Name), WebAgent);
}
}
public Listing Comments
{
get
{
if (Name == "/")
return new Listing(Reddit, "/comments.json", WebAgent);
return new Listing(Reddit, string.Format(CommentsUrl, Name), WebAgent);
}
}
public Listing New
{
get
{
if (Name == "/")
return new Listing(Reddit, "/new.json", WebAgent);
return new Listing(Reddit, string.Format(SubredditNewUrl, Name), WebAgent);
}
}
public Listing Hot
{
get
{
if (Name == "/")
return new Listing(Reddit, "/.json", WebAgent);
return new Listing(Reddit, string.Format(SubredditHotUrl, Name), WebAgent);
}
}
public Listing Rising
{
get
{
if (Name == "/")
return new Listing(Reddit, "/.json", WebAgent);
return new Listing(Reddit, string.Format(SubredditRisingUrl, Name), WebAgent);
}
}
public Listing ModQueue
{
get
{
return new Listing(Reddit, string.Format(ModqueueUrl, Name), WebAgent);
}
}
public Listing UnmoderatedLinks
{
get
{
return new Listing(Reddit, string.Format(UnmoderatedUrl, Name), WebAgent);
}
}
public Listing Search(string terms, Sorting sortE = Sorting.Relevance, TimeSorting timeE = TimeSorting.All)
{
string sort = sortE.ToString().ToLower();
string time = timeE.ToString().ToLower();
return new Listing(Reddit, string.Format(SearchUrl, Name, Uri.EscapeUriString(terms), sort, time), WebAgent);
}
public Listing Search(DateTime from, DateTime to, Sorting sortE = Sorting.New)
{
string sort = sortE.ToString().ToLower();
return new Listing(Reddit, string.Format(SearchUrlDate, Name, from.DateTimeToUnixTimestamp(), to.DateTimeToUnixTimestamp(), sort), 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();
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 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 IEnumerable UserNotes
{
get
{
return ToolBoxUserNotes.GetUserNotes(WebAgent, Name);
}
}
public Listing Contributors
{
get
{
return new Listing( Reddit, string.Format( ContributorsUrl, Name ), WebAgent );
}
}
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 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);
}
///
/// Submits a link post in the current subreddit using the logged-in user
///
/// The title of the submission
/// The url of the submission link
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
});
}
///
/// Submits a text post in the current subreddit using the logged-in user
///
/// The title of the submission
/// The raw markdown text of the submission
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
});
}
///
/// Gets the moderation log of the current subreddit
///
public Listing GetModerationLog()
{
return new Listing(Reddit, string.Format(ModLogUrl, this.Name), WebAgent);
}
///
/// Gets the moderation log of the current subreddit filtered by the action taken
///
/// ModActionType of action performed
public Listing GetModerationLog(ModActionType action)
{
return new Listing(Reddit, string.Format(ModLogUrl + "?type={1}", Name, ModActionTypeConverter.GetRedditParamName(action)), WebAgent);
}
///
/// Gets the moderation log of the current subreddit filtered by moderator(s) who performed the action
///
/// String array of mods to filter by
public Listing GetModerationLog(string[] mods)
{
return new Listing(Reddit, string.Format(ModLogUrl + "?mod={1}", Name, string.Join(",", mods)), WebAgent);
}
///
/// Gets the moderation log of the current subreddit filtered by the action taken and moderator(s) who performed the action
///
/// ModActionType of action performed
/// String array of mods to filter by
///
public Listing GetModerationLog(ModActionType action, string[] mods)
{
return new Listing(Reddit, string.Format(ModLogUrl + "?type={1}&mod={2}", Name, ModActionTypeConverter.GetRedditParamName(action), string.Join(",", mods)), WebAgent);
}
#region Obsolete Getter Methods
[Obsolete("Use Posts property instead")]
public Listing GetPosts()
{
return Posts;
}
[Obsolete("Use New property instead")]
public Listing GetNew()
{
return New;
}
[Obsolete("Use Hot property instead")]
public Listing GetHot()
{
return Hot;
}
[Obsolete("Use ModQueue property instead")]
public Listing GetModQueue()
{
return ModQueue;
}
[Obsolete("Use UnmoderatedLinks property instead")]
public Listing 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 GetModerators()
{
return Moderators;
}
#endregion Obsolete Getter Methods
}
}
================================================
FILE: RedditSharp/Things/Thing.cs
================================================
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();
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);
case "modaction":
return new ModAction().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(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);
}
else if (typeof(T) == typeof(ModAction))
{
return new ModAction().Init(reddit, json, webAgent);
}
else if (typeof(T) == typeof(Contributor))
{
return new Contributor().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();
Id = data["id"].ValueOrDefault();
Kind = json["kind"].ValueOrDefault();
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; }
///
/// The time at which this object was fetched from reddit servers.
///
public DateTime FetchedAt { get; private set; }
///
/// Gets the time since last fetch from reddit servers.
///
public TimeSpan TimeSinceFetch
{
get
{
return DateTime.Now - FetchedAt;
}
}
public static async Task ParseAsync(Reddit reddit, JToken json, IWebAgent webAgent)
{
var kind = json["kind"].ValueOrDefault();
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);
case "modaction":
return await new ModAction().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 ParseAsync(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);
}
else if (typeof(T) == typeof(ModAction))
{
return await new ModAction().InitAsync(reddit, json, webAgent);
}
}
return result;
}
}
}
================================================
FILE: RedditSharp/Things/VotableThing.cs
================================================
using System;
using System.Threading.Tasks;
using System.Linq;
using System.Security.Authentication;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace RedditSharp.Things
{
public class VotableThing : CreatedThing
{
public enum VoteType
{
Upvote = 1,
None = 0,
Downvote = -1
}
public enum ReportType
{
Spam = 0,
VoteManipulation = 1,
PersonalInformation = 2,
SexualizingMinors = 3,
BreakingReddit = 4,
Other = 5
}
public enum DistinguishType
{
Moderator,
Admin,
Special,
None
}
private const string VoteUrl = "/api/vote";
private const string SaveUrl = "/api/save";
private const string UnsaveUrl = "/api/unsave";
private const string ReportUrl = "/api/report";
private const string DistinguishUrl = "/api/distinguish";
[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 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("score")]
public int Score { get; set; }
[JsonProperty("saved")]
public bool Saved { get; set; }
[JsonProperty("distinguished")]
[JsonConverter(typeof(DistinguishConverter))]
public DistinguishType Distinguished { get; set; }
///
/// True if the logged in user has upvoted this.
/// False if they have not.
/// Null if they have not cast a vote.
///
[JsonProperty("likes")]
public bool? Liked { get; set; }
///
/// Gets or sets the vote for the current VotableThing.
///
[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());
}
public void Report(ReportType reportType, string otherReason = null)
{
var request = WebAgent.CreatePost(ReportUrl);
var stream = request.GetRequestStream();
string reportReason;
switch (reportType)
{
case ReportType.Spam:
reportReason = "spam"; break;
case ReportType.VoteManipulation:
reportReason = "vote manipulation"; break;
case ReportType.PersonalInformation:
reportReason = "personal information"; break;
case ReportType.BreakingReddit:
reportReason = "breaking reddit"; break;
case ReportType.SexualizingMinors:
reportReason = "sexualizing minors"; break;
default:
reportReason = "other"; break;
}
WebAgent.WritePostBody(stream, new
{
api_type = "json",
reason = reportReason,
other_reason = otherReason ?? "",
thing_id = FullName,
uh = Reddit.User.Modhash
});
stream.Close();
var response = request.GetResponse();
var data = WebAgent.GetResponseString(response.GetResponseStream());
}
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() == 11 && i[1].Value() == 12) == 0)
throw new AuthenticationException("You are not permitted to distinguish this comment.");
}
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();
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());
}
}
}
}
================================================
FILE: RedditSharp/Things/WikiPageRevision.cs
================================================
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 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);
}
}
}
================================================
FILE: RedditSharp/ToolBoxUserNotes.cs
================================================
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Compression;
namespace RedditSharp
{
public static class ToolBoxUserNotes
{
private const string ToolBoxUserNotesWiki = "/r/{0}/wiki/usernotes";
public static IEnumerable GetUserNotes(IWebAgent webAgent, string subName)
{
var request = webAgent.CreateGet(String.Format(ToolBoxUserNotesWiki, subName));
var reqResponse = webAgent.ExecuteRequest(request);
var response = JObject.Parse(reqResponse["data"]["content_md"].Value());
int version = response["ver"].Value();
string[] mods = response["constants"]["users"].Values().ToArray();
string[] warnings = response["constants"]["warnings"].Values().ToArray();
if (version < 6) throw new ToolBoxUserNotesException("Unsupported ToolBox version");
try
{
var data = Convert.FromBase64String(response["blob"].Value());
string uncompressed;
using (System.IO.MemoryStream compressedStream = new System.IO.MemoryStream(data))
{
compressedStream.ReadByte();
compressedStream.ReadByte(); //skips first to bytes to fix zlib block size
using (DeflateStream blobStream = new DeflateStream(compressedStream, CompressionMode.Decompress))
{
using (var decompressedReader = new System.IO.StreamReader(blobStream))
{
uncompressed = decompressedReader.ReadToEnd();
}
}
}
JObject users = JObject.Parse(uncompressed);
List toReturn = new List();
foreach (KeyValuePair user in users)
{
var x = user.Value;
foreach (JToken note in x["ns"].Children())
{
TBUserNote uNote = new TBUserNote();
uNote.AppliesToUsername = user.Key;
uNote.SubName = subName;
uNote.SubmitterIndex = note["m"].Value();
uNote.Submitter = mods[uNote.SubmitterIndex];
uNote.NoteTypeIndex = note["w"].Value();
uNote.NoteType = warnings[uNote.NoteTypeIndex];
uNote.Message = note["n"].Value();
uNote.Timestamp = UnixTimeStamp.UnixTimeStampToDateTime(note["t"].Value());
uNote.Url = UnsquashLink(subName, note["l"].ValueOrDefault());
toReturn.Add(uNote);
}
}
return toReturn;
}
catch (Exception e)
{
throw new ToolBoxUserNotesException("An error occured while processing Usernotes wiki. See inner exception for details", e);
}
}
public static string UnsquashLink(string subreddit, string permalink)
{
var link = "https://reddit.com/r/" + subreddit + "/";
if (string.IsNullOrEmpty(permalink))
{
return link;
}
var linkParams = permalink.Split(',');
if (linkParams[0] == "l")
{
link += "comments/" + linkParams[1] + "/";
if (linkParams.Length > 2)
link += "-/" + linkParams[2] + "/";
}
else if (linkParams[0] == "m")
{
link += "message/messages/" + linkParams[1];
}
return link;
}
}
}
================================================
FILE: RedditSharp/ToolBoxUserNotesException.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RedditSharp
{
class ToolBoxUserNotesException : Exception
{
public ToolBoxUserNotesException()
{
}
public ToolBoxUserNotesException(string message)
: base(message)
{
}
public ToolBoxUserNotesException(string message, Exception inner)
: base(message, inner)
{
}
}
}
================================================
FILE: RedditSharp/UnixTimeStamp.cs
================================================
using System;
namespace RedditSharp
{
public static class UnixTimeStamp
{
public static DateTime UnixTimeStampToDateTime(this long unixTimeStamp)
{
// Unix timestamp is seconds past epoch
var dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
dtDateTime = dtDateTime.AddSeconds(unixTimeStamp);
return dtDateTime;
}
}
}
================================================
FILE: RedditSharp/UnixTimestampConverter.cs
================================================
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace RedditSharp
{
public class UnixTimestampConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(double) || objectType == typeof(DateTime);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
return token.Value().UnixTimeStampToDateTime();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}
================================================
FILE: RedditSharp/UrlParser.cs
================================================
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace RedditSharp
{
class UrlParser : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(String) || objectType == typeof(Uri);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
if (token.Type == JTokenType.String)
{
if (Type.GetType("Mono.Runtime") == null)
return new Uri(token.Value(), UriKind.RelativeOrAbsolute);
if (token.Value().StartsWith("/"))
return new Uri(token.Value(), UriKind.Relative);
return new Uri(token.Value(), UriKind.RelativeOrAbsolute);
}
else
return token.Value();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
}
}
================================================
FILE: RedditSharp/Utils/DateTimeExtensions.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RedditSharp.Utils
{
internal static class DateTimeExtensions
{
public static double DateTimeToUnixTimestamp(this DateTime dateTime)
{
double time = (dateTime - new DateTime(1970, 1, 1).ToLocalTime()).TotalSeconds;
return Convert.ToInt32(time);
}
}
}
================================================
FILE: RedditSharp/WebAgent.cs
================================================
using System;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Web;
using Newtonsoft.Json.Linq;
namespace RedditSharp
{
public class WebAgent : IWebAgent
{
///
/// Additional values to append to the default RedditSharp user agent.
///
public static string UserAgent { get; set; }
///
/// It is strongly advised that you leave this enabled. Reddit bans excessive
/// requests with extreme predjudice.
///
public static bool EnableRateLimit { get; set; }
public static string Protocol { get; set; }
///
/// It is strongly advised that you leave this set to Burst or Pace. Reddit bans excessive
/// requests with extreme predjudice.
///
public static RateLimitMode RateLimit { get; set; }
///
/// The method by which the WebAgent will limit request rate
///
public enum RateLimitMode
{
///
/// Limits requests to one every two seconds
///
Pace,
///
/// Restricts requests to five per ten seconds
///
SmallBurst,
///
/// Restricts requests to thirty per minute
///
Burst,
///
/// Does not restrict request rate. ***NOT RECOMMENDED***
///
None
}
///
/// The root domain RedditSharp uses to address Reddit.
/// www.reddit.com by default
///
public static string RootDomain { get; set; }
///
/// Used to make calls against Reddit's API using OAuth23
///
public string AccessToken { get; set; }
public CookieContainer Cookies { get; set; }
public string AuthCookie { get; set; }
private static DateTime _lastRequest;
private static DateTime _burstStart;
private static int _requestsThisBurst;
///
/// UTC DateTime of last request made to Reddit API
///
public DateTime LastRequest
{
get { return _lastRequest; }
}
///
/// UTC DateTime of when the last burst started
///
public DateTime BurstStart
{
get { return _burstStart; }
}
///
/// Number of requests made during the current burst
///
public int RequestsThisBurst
{
get { return _requestsThisBurst; }
}
public virtual JToken CreateAndExecuteRequest(string url)
{
Uri uri;
if (!Uri.TryCreate(url, UriKind.Absolute, out uri))
{
if (!Uri.TryCreate(String.Format("{0}://{1}{2}", Protocol, RootDomain, url), UriKind.Absolute, out uri))
throw new Exception("Could not parse Uri");
}
var request = CreateGet(uri);
try { return ExecuteRequest(request); }
catch (Exception)
{
var tempProtocol = Protocol;
var tempRootDomain = RootDomain;
Protocol = "http";
RootDomain = "www.reddit.com";
var retval = CreateAndExecuteRequest(url);
Protocol = tempProtocol;
RootDomain = tempRootDomain;
return retval;
}
}
///
/// Executes the web request and handles errors in the response
///
///
///
public virtual JToken ExecuteRequest(HttpWebRequest request)
{
EnforceRateLimit();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
var result = GetResponseString(response.GetResponseStream());
JToken json;
if (!string.IsNullOrEmpty(result))
{
json = JToken.Parse(result);
try
{
if (json["json"] != null)
{
json = json["json"]; //get json object if there is a root node
}
if (json["error"] != null)
{
switch (json["error"].ToString())
{
case "404":
throw new Exception("File Not Found");
case "403":
throw new Exception("Restricted");
case "invalid_grant":
//Refresh authtoken
//AccessToken = authProvider.GetRefreshToken();
//ExecuteRequest(request);
break;
}
}
}
catch
{
}
}
else
{
json = JToken.Parse("{'method':'" + response.Method + "','uri':'" + response.ResponseUri.AbsoluteUri + "','status':'" + response.StatusCode.ToString() + "'}");
}
return json;
}
[MethodImpl(MethodImplOptions.Synchronized)]
protected virtual void EnforceRateLimit()
{
switch (RateLimit)
{
case RateLimitMode.Pace:
while ((DateTime.UtcNow - _lastRequest).TotalSeconds < 2)// Rate limiting
Thread.Sleep(250);
_lastRequest = DateTime.UtcNow;
break;
case RateLimitMode.SmallBurst:
if (_requestsThisBurst == 0 || (DateTime.UtcNow - _burstStart).TotalSeconds >= 10) //this is first request OR the burst expired
{
_burstStart = DateTime.UtcNow;
_requestsThisBurst = 0;
}
if (_requestsThisBurst >= 5) //limit has been reached
{
while ((DateTime.UtcNow - _burstStart).TotalSeconds < 10)
Thread.Sleep(250);
_burstStart = DateTime.UtcNow;
_requestsThisBurst = 0;
}
_lastRequest = DateTime.UtcNow;
_requestsThisBurst++;
break;
case RateLimitMode.Burst:
if (_requestsThisBurst == 0 || (DateTime.UtcNow - _burstStart).TotalSeconds >= 60) //this is first request OR the burst expired
{
_burstStart = DateTime.UtcNow;
_requestsThisBurst = 0;
}
if (_requestsThisBurst >= 30) //limit has been reached
{
while ((DateTime.UtcNow - _burstStart).TotalSeconds < 60)
Thread.Sleep(250);
_burstStart = DateTime.UtcNow;
_requestsThisBurst = 0;
}
_lastRequest = DateTime.UtcNow;
_requestsThisBurst++;
break;
}
}
public virtual HttpWebRequest CreateRequest(string url, string method)
{
EnforceRateLimit();
bool prependDomain;
// IsWellFormedUriString returns true on Mono for some reason when using a string like "/api/me"
if (Type.GetType("Mono.Runtime") != null)
prependDomain = !url.StartsWith("http://") && !url.StartsWith("https://");
else
prependDomain = !Uri.IsWellFormedUriString(url, UriKind.Absolute);
HttpWebRequest request;
if (prependDomain)
request = (HttpWebRequest)WebRequest.Create(String.Format("{0}://{1}{2}", Protocol, RootDomain, url));
else
request = (HttpWebRequest)WebRequest.Create(url);
request.CookieContainer = Cookies;
if (Type.GetType("Mono.Runtime") != null)
{
var cookieHeader = Cookies.GetCookieHeader(new Uri("http://reddit.com"));
request.Headers.Set("Cookie", cookieHeader);
}
if (RootDomain == "oauth.reddit.com")// use OAuth
{
request.Headers.Set("Authorization", "bearer " + AccessToken);//Must be included in OAuth calls
}
request.Method = method;
request.UserAgent = UserAgent + " - with RedditSharp by /u/sircmpwn";
return request;
}
protected virtual HttpWebRequest CreateRequest(Uri uri, string method)
{
EnforceRateLimit();
var request = (HttpWebRequest)WebRequest.Create(uri);
request.CookieContainer = Cookies;
if (Type.GetType("Mono.Runtime") != null)
{
var cookieHeader = Cookies.GetCookieHeader(new Uri("http://reddit.com"));
request.Headers.Set("Cookie", cookieHeader);
}
if (RootDomain == "oauth.reddit.com")// use OAuth
{
request.Headers.Set("Authorization", "bearer " + AccessToken);//Must be included in OAuth calls
}
request.Method = method;
request.UserAgent = UserAgent + " - with RedditSharp by /u/sircmpwn";
return request;
}
public virtual HttpWebRequest CreateGet(string url)
{
return CreateRequest(url, "GET");
}
private HttpWebRequest CreateGet(Uri url)
{
return CreateRequest(url, "GET");
}
public virtual HttpWebRequest CreatePost(string url)
{
var request = CreateRequest(url, "POST");
request.ContentType = "application/x-www-form-urlencoded";
return request;
}
public virtual string GetResponseString(Stream stream)
{
var data = new StreamReader(stream).ReadToEnd();
stream.Close();
return data;
}
public virtual void WritePostBody(Stream stream, object data, params string[] additionalFields)
{
var type = data.GetType();
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
string value = "";
foreach (var property in properties)
{
var attr = property.GetCustomAttributes(typeof(RedditAPINameAttribute), false).FirstOrDefault() as RedditAPINameAttribute;
string name = attr == null ? property.Name : attr.Name;
var entry = Convert.ToString(property.GetValue(data, null));
value += name + "=" + HttpUtility.UrlEncode(entry).Replace(";", "%3B").Replace("&", "%26") + "&";
}
for (int i = 0; i < additionalFields.Length; i += 2)
{
var entry = Convert.ToString(additionalFields[i + 1]) ?? string.Empty;
value += additionalFields[i] + "=" + HttpUtility.UrlEncode(entry).Replace(";", "%3B").Replace("&", "%26") + "&";
}
value = value.Remove(value.Length - 1); // Remove trailing &
var raw = Encoding.UTF8.GetBytes(value);
stream.Write(raw, 0, raw.Length);
stream.Close();
}
}
}
================================================
FILE: RedditSharp/Wiki.cs
================================================
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;
using RedditSharp.Things;
namespace RedditSharp
{
using System;
public class Wiki
{
private Reddit Reddit { get; set; }
private Subreddit Subreddit { get; set; }
private IWebAgent WebAgent { get; set; }
private const string GetWikiPageUrl = "/r/{0}/wiki/{1}.json?v={2}";
private const string GetWikiPagesUrl = "/r/{0}/wiki/pages.json";
private const string WikiPageEditUrl = "/r/{0}/api/wiki/edit";
private const string HideWikiPageUrl = "/r/{0}/api/wiki/hide";
private const string RevertWikiPageUrl = "/r/{0}/api/wiki/revert";
private const string WikiPageAllowEditorAddUrl = "/r/{0}/api/wiki/alloweditor/add";
private const string WikiPageAllowEditorDelUrl = "/r/{0}/api/wiki/alloweditor/del";
private const string WikiPageSettingsUrl = "/r/{0}/wiki/settings/{1}.json";
private const string WikiRevisionsUrl = "/r/{0}/wiki/revisions.json";
private const string WikiPageRevisionsUrl = "/r/{0}/wiki/revisions/{1}.json";
private const string WikiPageDiscussionsUrl = "/r/{0}/wiki/discussions/{1}.json";
public IEnumerable PageNames
{
get
{
var request = WebAgent.CreateGet(string.Format(GetWikiPagesUrl, Subreddit.Name));
var response = request.GetResponse();
string json = WebAgent.GetResponseString(response.GetResponseStream());
return JObject.Parse(json)["data"].Values();
}
}
public Listing Revisions
{
get
{
return new Listing(Reddit, string.Format(WikiRevisionsUrl, Subreddit.Name), WebAgent);
}
}
protected internal Wiki(Reddit reddit, Subreddit subreddit, IWebAgent webAgent)
{
Reddit = reddit;
Subreddit = subreddit;
WebAgent = webAgent;
}
public WikiPage GetPage(string page, string version = null)
{
var request = WebAgent.CreateGet(string.Format(GetWikiPageUrl, Subreddit.Name, page, version));
var response = request.GetResponse();
var json = JObject.Parse(WebAgent.GetResponseString(response.GetResponseStream()));
var result = new WikiPage(Reddit, json["data"], WebAgent);
return result;
}
#region Settings
public WikiPageSettings GetPageSettings(string name)
{
var request = WebAgent.CreateGet(string.Format(WikiPageSettingsUrl, Subreddit.Name, name));
var response = request.GetResponse();
var json = JObject.Parse(WebAgent.GetResponseString(response.GetResponseStream()));
var result = new WikiPageSettings(Reddit, json["data"], WebAgent);
return result;
}
public void SetPageSettings(string name, WikiPageSettings settings)
{
var request = WebAgent.CreatePost(string.Format(WikiPageSettingsUrl, Subreddit.Name, name));
WebAgent.WritePostBody(request.GetRequestStream(), new
{
page = name,
permlevel = settings.PermLevel,
listed = settings.Listed,
uh = Reddit.User.Modhash
});
var response = request.GetResponse();
}
#endregion
#region Revisions
public Listing GetPageRevisions(string page)
{
return new Listing(Reddit, string.Format(WikiPageRevisionsUrl, Subreddit.Name, page), WebAgent);
}
#endregion
#region Discussions
public Listing GetPageDiscussions(string page)
{
return new Listing(Reddit, string.Format(WikiPageDiscussionsUrl, Subreddit.Name, page), WebAgent);
}
#endregion
public void EditPage(string page, string content, string previous = null, string reason = null)
{
var request = WebAgent.CreatePost(string.Format(WikiPageEditUrl, Subreddit.Name));
dynamic param = new
{
content = content,
page = page,
uh = Reddit.User.Modhash
};
List addParams = new List();
if (previous != null)
{
addParams.Add("previous");
addParams.Add(previous);
}
if (reason != null)
{
addParams.Add("reason");
addParams.Add(reason);
}
WebAgent.WritePostBody(request.GetRequestStream(), param,addParams.ToArray());
var response = request.GetResponse();
}
public void HidePage(string page, string revision)
{
var request = WebAgent.CreatePost(string.Format(HideWikiPageUrl, Subreddit.Name));
WebAgent.WritePostBody(request.GetRequestStream(), new
{
page = page,
revision = revision,
uh = Reddit.User.Modhash
});
var response = request.GetResponse();
}
public void RevertPage(string page, string revision)
{
var request = WebAgent.CreatePost(string.Format(RevertWikiPageUrl, Subreddit.Name));
WebAgent.WritePostBody(request.GetRequestStream(), new
{
page = page,
revision = revision,
uh = Reddit.User.Modhash
});
var response = request.GetResponse();
}
public void SetPageEditor(string page, string username, bool allow)
{
var request = WebAgent.CreatePost(string.Format(allow ? WikiPageAllowEditorAddUrl : WikiPageAllowEditorDelUrl, Subreddit.Name));
WebAgent.WritePostBody(request.GetRequestStream(), new
{
page = page,
username = username,
uh = Reddit.User.Modhash
});
var response = request.GetResponse();
}
#region Obsolete Getter Methods
[Obsolete("Use PageNames property instead")]
public IEnumerable GetPageNames()
{
return PageNames;
}
[Obsolete("Use Revisions property instead")]
public Listing GetRevisions()
{
return Revisions;
}
#endregion Obsolete Getter Methods
}
}
================================================
FILE: RedditSharp/WikiPage.cs
================================================
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using RedditSharp.Things;
namespace RedditSharp
{
public class WikiPage
{
[JsonProperty("may_revise")]
public string MayRevise { get; set; }
[JsonProperty("revision_date")]
[JsonConverter(typeof(UnixTimestampConverter))]
public DateTime? RevisionDate { get; set; }
[JsonProperty("content_html")]
public string HtmlContent { get; set; }
[JsonProperty("content_md")]
public string MarkdownContent { get; set; }
[JsonIgnore]
public RedditUser RevisionBy { get; set; }
protected internal WikiPage(Reddit reddit, JToken json, IWebAgent webAgent)
{
RevisionBy = new RedditUser().Init(reddit, json["revision_by"], webAgent);
JsonConvert.PopulateObject(json.ToString(), this, reddit.JsonSerializerSettings);
}
}
}
================================================
FILE: RedditSharp/WikiPageSettings.cs
================================================
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;
using RedditSharp.Things;
namespace RedditSharp
{
public class WikiPageSettings
{
[JsonProperty("listed")]
public bool Listed { get; set; }
[JsonProperty("permlevel")]
public int PermLevel { get; set; }
[JsonIgnore]
public IEnumerable Editors { get; set; }
public WikiPageSettings()
{
}
protected internal WikiPageSettings(Reddit reddit, JToken json, IWebAgent webAgent)
{
var editors = json["editors"].ToArray();
Editors = editors.Select(x => new RedditUser().Init(reddit, x, webAgent));
JsonConvert.PopulateObject(json.ToString(), this, reddit.JsonSerializerSettings);
}
}
}
================================================
FILE: RedditSharp.sln
================================================
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedditSharp", "RedditSharp\RedditSharp.csproj", "{A368CB75-75F0-4489-904D-B5CEBB0FE624}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestRedditSharp", "TestRedditSharp\TestRedditSharp.csproj", "{0101B252-0CCE-4572-8882-D5851FE41E4F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0101B252-0CCE-4572-8882-D5851FE41E4F}.Debug|Any CPU.ActiveCfg = Debug|x86
{0101B252-0CCE-4572-8882-D5851FE41E4F}.Debug|Any CPU.Build.0 = Debug|x86
{0101B252-0CCE-4572-8882-D5851FE41E4F}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{0101B252-0CCE-4572-8882-D5851FE41E4F}.Debug|Mixed Platforms.Build.0 = Debug|x86
{0101B252-0CCE-4572-8882-D5851FE41E4F}.Debug|x86.ActiveCfg = Debug|x86
{0101B252-0CCE-4572-8882-D5851FE41E4F}.Debug|x86.Build.0 = Debug|x86
{0101B252-0CCE-4572-8882-D5851FE41E4F}.Release|Any CPU.ActiveCfg = Release|x86
{0101B252-0CCE-4572-8882-D5851FE41E4F}.Release|Mixed Platforms.ActiveCfg = Release|x86
{0101B252-0CCE-4572-8882-D5851FE41E4F}.Release|Mixed Platforms.Build.0 = Release|x86
{0101B252-0CCE-4572-8882-D5851FE41E4F}.Release|x86.ActiveCfg = Release|x86
{0101B252-0CCE-4572-8882-D5851FE41E4F}.Release|x86.Build.0 = Release|x86
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Debug|x86.ActiveCfg = Debug|Any CPU
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Debug|x86.Build.0 = Debug|Any CPU
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Release|Any CPU.Build.0 = Release|Any CPU
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Release|x86.ActiveCfg = Release|Any CPU
{A368CB75-75F0-4489-904D-B5CEBB0FE624}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = TestRedditSharp\TestRedditSharp.csproj
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
================================================
FILE: TestRedditSharp/App.config
================================================
================================================
FILE: TestRedditSharp/Program.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using RedditSharp;
using System.Security.Authentication;
using RedditSharp.Things;
namespace TestRedditSharp
{
class Program
{
static void Main(string[] args)
{
Reddit reddit = null;
var authenticated = false;
while (!authenticated)
{
Console.Write("OAuth? (y/n) [n]: ");
var oaChoice = Console.ReadLine();
if (!string.IsNullOrEmpty(oaChoice) && oaChoice.ToLower()[0] == 'y')
{
Console.Write("OAuth token: ");
var token = Console.ReadLine();
reddit = new Reddit(token);
reddit.InitOrUpdateUser();
authenticated = reddit.User != null;
if (!authenticated)
Console.WriteLine("Invalid token");
}
else
{
Console.Write("Username: ");
var username = Console.ReadLine();
Console.Write("Password: ");
var password = ReadPassword();
try
{
Console.WriteLine("Logging in...");
reddit = new Reddit(username, password);
authenticated = reddit.User != null;
}
catch (AuthenticationException)
{
Console.WriteLine("Incorrect login.");
authenticated = false;
}
}
}
/*Console.Write("Create post? (y/n) [n]: ");
var choice = Console.ReadLine();
if (!string.IsNullOrEmpty(choice) && choice.ToLower()[0] == 'y')
{
Console.Write("Type a subreddit name: ");
var subname = Console.ReadLine();
var sub = reddit.GetSubreddit(subname);
Console.WriteLine("Making test post");
var post = sub.SubmitTextPost("RedditSharp test", "This is a test post sent from RedditSharp");
Console.WriteLine("Submitted: {0}", post.Url);
}
else
{
Console.Write("Type a subreddit name: ");
var subname = Console.ReadLine();
var sub = reddit.GetSubreddit(subname);
foreach (var post in sub.GetTop(FromTime.Week).Take(10))
Console.WriteLine("\"{0}\" by {1}", post.Title, post.Author);
}*/
Comment comment = (Comment)reddit.GetThingByFullname("t1_ciif2g7");
Post post = (Post)reddit.GetThingByFullname("t3_298g7j");
PrivateMessage pm = (PrivateMessage)reddit.GetThingByFullname("t4_20oi3a"); // Use your own PM here, as you don't have permission to view this one
Console.WriteLine(comment.Body);
Console.WriteLine(post.Title);
Console.WriteLine(pm.Body);
Console.WriteLine(post.Comment("test").FullName);
Console.ReadKey(true);
}
public static string ReadPassword()
{
var passbits = new Stack();
//keep reading
for (ConsoleKeyInfo cki = Console.ReadKey(true); cki.Key != ConsoleKey.Enter; cki = Console.ReadKey(true))
{
if (cki.Key == ConsoleKey.Backspace)
{
if (passbits.Count() > 0)
{
//rollback the cursor and write a space so it looks backspaced to the user
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
Console.Write(" ");
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
passbits.Pop();
}
}
else
{
Console.Write("*");
passbits.Push(cki.KeyChar.ToString());
}
}
string[] pass = passbits.ToArray();
Array.Reverse(pass);
Console.Write(Environment.NewLine);
return string.Join(string.Empty, pass);
}
}
}
================================================
FILE: TestRedditSharp/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("TestRedditSharp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TestRedditSharp")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ca7b9ba0-4e47-45d3-aeca-4665957ae929")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
================================================
FILE: TestRedditSharp/TestRedditSharp.csproj
================================================
Debug
x86
8.0.30703
2.0
{0101B252-0CCE-4572-8882-D5851FE41E4F}
Exe
Properties
TestRedditSharp
TestRedditSharp
512
v4.5
x86
True
full
False
bin\Debug\
DEBUG;TRACE
prompt
4
true
false
x86
pdbonly
True
bin\Release\
TRACE
prompt
4
false
{A368CB75-75F0-4489-904D-B5CEBB0FE624}
RedditSharp