Last active
May 23, 2019 06:05
-
-
Save 0x00000FF/3a4ba00dfee15a9062b7aff491d13d31 to your computer and use it in GitHub Desktop.
Everytime Article Fetch/Delete Class
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Article/Comment Deletion Agent for Everytime service | |
* Available for .NET Framework/Core | |
* | |
* Newtonsoft.Json Required for Json Deserialization | |
* Json Types for Deserialization is under the Cleaner class | |
*/ | |
using System.Collections.Generic; | |
using System.Text; | |
using System.Net; | |
using System.IO; | |
using System.Xml; | |
using Newtonsoft.Json; | |
namespace EverytimeCleanerCore | |
{ | |
/// <summary> | |
/// Main abstraction of Cleaner, Fetch and Delete implemented | |
/// </summary> | |
class Cleaner | |
{ | |
/// <summary> | |
/// Cookie container for HttpWebRequest | |
/// </summary> | |
CookieContainer cc; | |
/// <summary> | |
/// ETag seems the name of JWT, need to be stored. | |
/// </summary> | |
string etag; | |
/// <summary> | |
/// Alias for UTF-8 Encoding | |
/// </summary> | |
static readonly Encoding enc = Encoding.UTF8; | |
/// <summary> | |
/// Is Cleaner available? | |
/// </summary> | |
public bool Status { get; set; } | |
/// <summary> | |
/// Constructor | |
/// </summary> | |
/// <param name="id">Username</param> | |
/// <param name="password">Password</param> | |
public Cleaner(string id, string password) | |
{ | |
var wr = CreateHttpRequest("https://everytime.kr/user/login"); | |
WritePostData(wr, $"userid={id}&password={password}&redirect=%2F"); | |
try | |
{ | |
var res = (HttpWebResponse)wr.GetResponse(); | |
var html = ReadAllFromResponse(res); | |
// Everytime API does not inform you to success or not with status code, but redirect to main page or display error message. | |
if (html.Contains("아이디나 비밀번호를 바르게 입력해주세요.") || html.Contains("알수없는 오류가 발생하였습니다.")) | |
{ | |
Status = false; | |
} | |
else | |
{ | |
etag = res.Headers["ETag"]; | |
Status = true; | |
} | |
} | |
catch (WebException) | |
{ | |
Status = false; | |
} | |
} | |
/// <summary> | |
/// Creates set of header entities for each request | |
/// </summary> | |
/// <returns>WebheaderCollection</returns> | |
WebHeaderCollection CreateRequestHeader() | |
{ | |
var header = new WebHeaderCollection | |
{ | |
{ "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" }, | |
{ "Accept-Language", "en-US,en;q=0.9" }, | |
{ "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36" }, | |
{ "X-Requested-With", "XMLHttpRequest" } | |
}; | |
if (etag != null) | |
header.Add("ETag", etag); | |
return header; | |
} | |
/// <summary> | |
/// Write string data into request stream in UTF-8 encoding. | |
/// </summary> | |
/// <param name="wr">Request object</param> | |
/// <param name="str">String data to be written</param> | |
void WritePostData(HttpWebRequest wr, string str) | |
{ | |
if (wr.Method != "POST") wr.Method = "POST"; | |
wr.Referer = "https://everytime.kr"; | |
wr.ContentType = "application/x-www-form-urlencoded; charset=UTF-8"; | |
using (var stream = wr.GetRequestStream()) | |
{ | |
var sendData = enc.GetBytes(str); | |
stream.Write(sendData); | |
wr.ContentLength = sendData.Length; | |
} | |
} | |
/// <summary> | |
/// Create HttpWebRequest object that contains data should be contained in common. | |
/// </summary> | |
/// <param name="uri">URI to be requested</param> | |
/// <param name="cc">CookieContainer. If null, will create new one.</param> | |
/// <returns>Returns a HttpWebRequest object</returns> | |
HttpWebRequest CreateHttpRequest(string uri, CookieContainer cc = null) | |
{ | |
var wr = WebRequest.CreateHttp(uri); | |
wr.Headers = CreateRequestHeader(); | |
wr.CookieContainer = cc ?? new CookieContainer(); | |
wr.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; | |
return wr; | |
} | |
/// <summary> | |
/// Read all data in string from response | |
/// </summary> | |
/// <param name="response">Response object to be read</param> | |
/// <returns>Returns string data if succeed</returns> | |
string ReadAllFromResponse(HttpWebResponse response) | |
{ | |
using (var res = response) | |
{ | |
Encoding encoding; | |
try { encoding = Encoding.GetEncoding(res.ContentEncoding); } | |
catch (ArgumentException) { encoding = enc; } | |
using (var reader = new StreamReader(res.GetResponseStream(), encoding)) | |
{ | |
try { return reader.ReadToEnd(); } | |
catch { return null; } | |
} | |
} | |
} | |
/// <summary> | |
/// Deserialize given XML into Root object. | |
/// Convert to XML is not required, you can deserialize directly from XML. | |
/// </summary> | |
/// <param name="data">XML string</param> | |
/// <returns>Root object that contains a collection of articles if succeed</returns> | |
ResponseRoot DeserializeResult(string data) | |
{ | |
var doc = new XmlDocument(); | |
doc.LoadXml(data); | |
var sr = JsonConvert.SerializeXmlNode(doc.ChildNodes[1], Newtonsoft.Json.Formatting.Indented); | |
return JsonConvert.DeserializeObject<ResponseRoot>(sr); | |
} | |
/// <summary> | |
/// Fetch all of articles for specified types | |
/// </summary> | |
/// <param name="type">Type of article(including comments), default is myarticle</param> | |
/// <returns>Actual list of article if succeed</returns> | |
public List<Article> FetchArticles(string type = "myarticle") | |
{ | |
var startNum = 0; | |
var articles = new List<Article>(); | |
// Everytime API does not emit over 20 articles at once, so fetching process need to be repeated. | |
while (true) | |
{ | |
var req = CreateHttpRequest("https://everytime.kr/find/board/article/list", cc); | |
WritePostData(req, $"id={type}&limit_num=20&start_num={startNum}&moiminfo=true"); | |
var res = (HttpWebResponse)req.GetResponse(); | |
var html = ReadAllFromResponse(res); | |
var art = DeserializeResult(html).response.article; | |
articles.AddRange(art); | |
if (art.Length < 20) break; | |
else startNum += 20; | |
} | |
return articles; | |
} | |
/// <summary> | |
/// Delete article/comment for specified type and id. | |
/// </summary> | |
/// <param name="type">type of article/comment</param> | |
/// <param name="id">Article/Comment id</param> | |
/// <returns>Returns true if succeed</returns> | |
public bool DeleteArticle(string type, int id) | |
{ | |
var req = CreateHttpRequest($"https://everytime.kr/remove/board/{type}", cc); | |
WritePostData(req, $"id={id}"); | |
var res = (HttpWebResponse)req.GetResponse(); | |
return ReadAllFromResponse(res).Contains("<response>1</response>"); | |
} | |
} | |
// -------------------------------------------------------------------------------------------- | |
public class ResponseRoot | |
{ | |
public Response response { get;set; } | |
} | |
public class Response | |
{ | |
public Article[] article { get; set; } | |
} | |
public class Article | |
{ | |
[JsonProperty(PropertyName = "@id")] | |
public string id { get; set; } | |
[JsonProperty(PropertyName = "@is_mine")] | |
public string is_mine { get; set; } | |
[JsonProperty(PropertyName = "@title")] | |
public string title { get; set; } | |
[JsonProperty(PropertyName = "@text")] | |
public string text { get; set; } | |
[JsonProperty(PropertyName = "@created_at")] | |
public string created_at { get; set; } | |
[JsonProperty(PropertyName = "@posvote")] | |
public string posvote { get; set; } | |
[JsonProperty(PropertyName = "@comment")] | |
public string comment { get; set; } | |
[JsonProperty(PropertyName = "@comment_anonym")] | |
public string comment_anonym { get; set; } | |
[JsonProperty(PropertyName = "@scrap_count")] | |
public string scrap_count { get; set; } | |
[JsonProperty(PropertyName = "@board_id")] | |
public string board_id { get; set; } | |
[JsonProperty(PropertyName = "@board_name")] | |
public string board_name { get; set; } | |
[JsonProperty(PropertyName = "@user_type")] | |
public string user_type { get; set; } | |
[JsonProperty(PropertyName = "@user_id")] | |
public string user_id { get; set; } | |
[JsonProperty(PropertyName = "@user_nickname")] | |
public string user_nickname { get; set; } | |
[JsonProperty(PropertyName = "@user_picture")] | |
public string user_picture { get; set; } | |
[JsonProperty(PropertyName = "@attach")] | |
public object attach { get; set; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Completed try-catch block through Ln 144 to Ln 151