First actual commit
added sources to repository
This commit is contained in:
371
RedditSharp/Reddit.cs
Normal file
371
RedditSharp/Reddit.cs
Normal file
@@ -0,0 +1,371 @@
|
||||
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;
|
||||
|
||||
namespace RedditSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to communicate with Reddit.com
|
||||
/// </summary>
|
||||
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}.json";
|
||||
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}'";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Variables
|
||||
|
||||
static Reddit()
|
||||
{
|
||||
WebAgent.UserAgent = "";
|
||||
WebAgent.RateLimit = WebAgent.RateLimitMode.Pace;
|
||||
WebAgent.Protocol = "http";
|
||||
WebAgent.RootDomain = "www.reddit.com";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal readonly IWebAgent _webAgent;
|
||||
|
||||
/// <summary>
|
||||
/// Captcha solver instance to use when solving captchas.
|
||||
/// </summary>
|
||||
public ICaptchaSolver CaptchaSolver;
|
||||
|
||||
/// <summary>
|
||||
/// The authenticated user for this instance.
|
||||
/// </summary>
|
||||
public AuthenticatedUser User { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Rate Limiting Mode of the underlying WebAgent
|
||||
/// </summary>
|
||||
public WebAgent.RateLimitMode RateLimit
|
||||
{
|
||||
get { return WebAgent.RateLimit; }
|
||||
set { WebAgent.RateLimit = value; }
|
||||
}
|
||||
|
||||
internal JsonSerializerSettings JsonSerializerSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the FrontPage using the current Reddit instance.
|
||||
/// </summary>
|
||||
public Subreddit FrontPage
|
||||
{
|
||||
get { return Subreddit.GetFrontPage(this); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets /r/All using the current Reddit instance.
|
||||
/// </summary>
|
||||
public Subreddit RSlashAll
|
||||
{
|
||||
get { return Subreddit.GetRSlashAll(this); }
|
||||
}
|
||||
|
||||
public Reddit()
|
||||
{
|
||||
JsonSerializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
CheckAdditionalContent = false,
|
||||
DefaultValueHandling = DefaultValueHandling.Ignore
|
||||
};
|
||||
_webAgent = new WebAgent();
|
||||
CaptchaSolver = new ConsoleCaptchaSolver();
|
||||
}
|
||||
|
||||
public Reddit(WebAgent.RateLimitMode limitMode) : this()
|
||||
{
|
||||
WebAgent.UserAgent = "";
|
||||
WebAgent.RateLimit = limitMode;
|
||||
WebAgent.RootDomain = "www.reddit.com";
|
||||
}
|
||||
|
||||
public Reddit(string username, string password, bool useSsl = true) : this()
|
||||
{
|
||||
LogIn(username, password, useSsl);
|
||||
}
|
||||
|
||||
public Reddit(string accessToken) : this()
|
||||
{
|
||||
WebAgent.Protocol = "https";
|
||||
WebAgent.RootDomain = OAuthDomainUrl;
|
||||
_webAgent.AccessToken = accessToken;
|
||||
InitOrUpdateUser();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs in the current Reddit instance.
|
||||
/// </summary>
|
||||
/// <param name="username">The username of the user to log on to.</param>
|
||||
/// <param name="password">The password of the user to log on to.</param>
|
||||
/// <param name="useSsl">Whether to use SSL or not. (default: true)</param>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the User property if it's null,
|
||||
/// otherwise replaces the existing user object
|
||||
/// with a new one fetched from reddit servers.
|
||||
/// </summary>
|
||||
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);
|
||||
return GetThing<Subreddit>(string.Format(SubredditAboutUrl, name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the subreddit.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the subreddit</param>
|
||||
/// <returns>The Subreddit by given name</returns>
|
||||
public async Task<Subreddit> GetSubredditAsync(string name)
|
||||
{
|
||||
if (name.StartsWith("r/"))
|
||||
name = name.Substring(2);
|
||||
if (name.StartsWith("/r/"))
|
||||
name = name.Substring(3);
|
||||
return await GetThingAsync<Subreddit>(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);
|
||||
}
|
||||
|
||||
public void ComposePrivateMessage(string subject, string body, string to, string captchaId = "", string captchaAnswer = "")
|
||||
{
|
||||
if (User == null)
|
||||
throw new Exception("User can not be null.");
|
||||
var request = _webAgent.CreatePost(ComposeMessageUrl);
|
||||
_webAgent.WritePostBody(request.GetRequestStream(), new
|
||||
{
|
||||
api_type = "json",
|
||||
subject,
|
||||
text = body,
|
||||
to,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new Reddit user
|
||||
/// </summary>
|
||||
/// <param name="userName">The username for the new account.</param>
|
||||
/// <param name="passwd">The password for the new account.</param>
|
||||
/// <param name="email">The optional recovery email for the new account.</param>
|
||||
/// <returns>The newly created user account</returns>
|
||||
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 request = _webAgent.CreateGet(string.Format(GetCommentUrl, subreddit, linkName, name));
|
||||
var response = request.GetResponse();
|
||||
var data = _webAgent.GetResponseString(response.GetResponseStream());
|
||||
var json = JToken.Parse(data);
|
||||
return Thing.Parse(this, json[1]["data"]["children"][0], _webAgent) as Comment;
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Listing<T> SearchByUrl<T>(string url) where T : Thing
|
||||
{
|
||||
var urlSearchQuery = string.Format(UrlSearchPattern, url);
|
||||
return Search<T>(urlSearchQuery);
|
||||
}
|
||||
|
||||
public Listing<T> Search<T>(string query) where T : Thing
|
||||
{
|
||||
return new Listing<T>(this, string.Format(SearchUrl, query, "relevance", "all"), _webAgent);
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
protected async internal Task<T> GetThingAsync<T>(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<T>(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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user