/SauceHunt/Program.cs
C# | 291 lines | 283 code | 5 blank | 3 comment | 60 complexity | 505c7b337fab16572dfc77b4fff88cb7 MD5 | raw file
- using System;
- using System.IO;
- using Newtonsoft.Json;
- using RedditSharp;
- using System.Threading;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using Newtonsoft.Json.Linq;
- using SauceHunt.Database;
- using NHibernate.Linq;
- using Database;
- namespace SauceHunt
- {
- class MainClass
- {
- public static Configuration Config;
- public static SauceDatabase SauceDatabase;
- public static void Main(string[] args)
- {
- Config = new Configuration();
- var configPath = "config.json";
- if (args.Length != 0)
- configPath = args[0];
- if (File.Exists(configPath))
- JsonConvert.PopulateObject(File.ReadAllText(configPath), Config);
- else
- {
- File.WriteAllText(configPath, JsonConvert.SerializeObject(Config, Formatting.Indented));
- Console.WriteLine(configPath);
- return;
- }
- File.WriteAllText(configPath, JsonConvert.SerializeObject(Config, Formatting.Indented));
- if (string.IsNullOrEmpty(Config.PostgreConnectionString))
- {
- Console.WriteLine("Needs a connection string.");
- return;
- }
- SauceDatabase = new SauceDatabase(Config.PostgreConnectionString);
- var reddit = new Reddit();
- reddit.LogIn(Config.RedditUsername, Config.RedditPassword);
- var subreddits = new List<Subreddit>();
- foreach (var sr in Config.Subreddits)
- subreddits.Add(reddit.GetSubreddit(sr.Name));
- while (true)
- {
- Console.WriteLine("Running update at {0}", DateTime.Now.ToShortTimeString());
- foreach (var _message in reddit.User.GetUnreadMessages())
- {
- if (_message is PrivateMessage)
- {
- var message = (PrivateMessage)_message;
- using (var session = SauceDatabase.SessionFactory.OpenSession())
- {
- var user = session.Query<User>().SingleOrDefault(u => u.Name == message.Author);
- if (user == null)
- {
- user = new User
- {
- Name = message.Author,
- SkipWaitPeriod = false,
- IgnoreUser = false,
- WarnIfNoSauce = false,
- Posts = new List<RedditPost>()
- };
- using (var transaction = session.BeginTransaction())
- {
- session.SaveOrUpdate(user);
- transaction.Commit();
- }
- }
- message.SetAsRead();
- if (message.Body.ToUpper().Contains("RESPOND IMMEDIATELY"))
- {
- user.SkipWaitPeriod = true;
- message.Reply("Okay, from now on, I will find the source for your posts as soon as I see them.");
- }
- else if (message.Body.ToUpper().Contains("SLOW DOWN"))
- {
- user.SkipWaitPeriod = false;
- message.Reply("I'll take things a little slower now.");
- }
- else if (message.Body.ToUpper().Contains("HELP ME FIND SAUCE"))
- {
- user.IgnoreUser = true;
- message.Reply("Sorry, I won't bother you any more.");
- }
- else if (message.Body.ToUpper().Contains("NOTICE ME"))
- {
- user.IgnoreUser = false;
- message.Reply("I'll try to pay more attention to you now.");
- }
- if (message.Body.ToUpper().Contains("WATCH OUT"))
- {
- user.WarnIfNoSauce = true;
- message.Reply("I've got your back, babe.");
- }
- if (message.Body.ToUpper().Contains("WARN ME"))
- {
- user.WarnIfNoSauce = false;
- message.Reply("Sorry, I won't look out for you anymore.");
- }
- using (var transaction = session.BeginTransaction())
- {
- session.SaveOrUpdate(user);
- transaction.Commit();
- }
- }
- }
- }
- foreach (var subreddit in subreddits)
- {
- var sr = Config.Subreddits.Single(s => s.Name.ToUpper().Replace("/R/", "") == subreddit.Name.ToUpper());
- var posts = subreddit.GetNew().Take(50).ToArray();
- foreach (var post in posts)
- {
- using (var session = SauceDatabase.SessionFactory.OpenSession())
- {
- if (post.IsSelfPost)
- continue;
- if (session.Query<RedditPost>().Any(p => p.RedditId == post.Id))
- continue;
- var user = session.Query<User>().SingleOrDefault(u => u.Name == post.AuthorName);
- if (user == null)
- {
- user = new User
- {
- Name = post.AuthorName,
- SkipWaitPeriod = false,
- IgnoreUser = false,
- Posts = new List<RedditPost>()
- };
- using (var transaction = session.BeginTransaction())
- {
- session.Save(user);
- transaction.Commit();
- }
- }
- try
- {
- var diff = DateTime.Now - post.Created;
- Console.WriteLine("Seeing post {1} from {0} minutes ago at {2}", Math.Ceiling(diff.TotalMinutes), post.Id, post.Created.ToString());
- bool sauceFound = false;
- if (user.SkipWaitPeriod || sr.SauceImmediately || (diff.TotalMinutes > 30 && diff.TotalMinutes < 60))
- {
- if (!user.IgnoreUser)
- {
- sauceFound = HandlePost(post);
- if (!sauceFound && user.WarnIfNoSauce)
- {
- reddit.ComposePrivateMessage("Warning: unable to locate sauce", "I was not able to locate sauce for [this post](" + post.Shortlink + ").", post.AuthorName);
- }
- var dbPost = new RedditPost
- {
- SauceFound = sauceFound,
- RedditId = post.Id,
- Submitter = user
- };
- user.Posts.Add(dbPost);
- using (var transaction = session.BeginTransaction())
- {
- session.SaveOrUpdate(dbPost);
- session.SaveOrUpdate(user);
- transaction.Commit();
- }
- }
- }
- }
- catch (Exception e)
- {
- Console.WriteLine("Error handling {0}", post.Id);
- if (!session.Query<RedditPost>().Any(p => p.RedditId == post.Id))
- {
- var dbPost = new RedditPost
- {
- SauceFound = false,
- RedditId = post.Id,
- Submitter = user
- };
- user.Posts.Add(dbPost);
- using (var transaction = session.BeginTransaction())
- {
- session.Save(dbPost);
- session.Save(user);
- transaction.Commit();
- }
- }
- }
- }
- }
- }
- Console.WriteLine("Done.");
- Thread.Sleep(Config.Interval * 60 * 1000);
- }
- }
- public static bool HandlePost(Post post)
- {
- // Make sure there's an image there
- var url = post.Url;
- var request = (HttpWebRequest)WebRequest.Create(url);
- request.Method = "HEAD";
- var response = request.GetResponse();
- if (!(response.ContentType == "image/png" || response.ContentType == "image/jpeg" || response.ContentType == "image/bmp"))
- {
- if (!DomainMaps(ref url))
- return false;
- }
- // Check to see if someone already posted some sauce
- var comments = post.GetComments();
- foreach (var comment in comments)
- {
- var text = comment.Body.ToUpper();
- if (text.Contains("SOURCE") || text.Contains("SAUCE") || text.Contains("ARTIST") ||
- text.Contains("PIXIV"))
- {
- return false;
- }
- }
- // I need me some sauce
- var sauce = GetSauce(url);
- if (sauce == null)
- return false;
- Console.WriteLine("Found sauce for {0}", post.Id);
- var check = (HttpWebRequest)WebRequest.Create(sauce.Item2);
- check.Method = "HEAD";
- try
- {
- check.GetResponse();
- }
- catch
- {
- Console.WriteLine("But it was borked.");
- return false;
- }
- var reply = string.Format(Config.CommentTemplate, sauce.Item1, sauce.Item2);
- while (true)
- {
- try
- {
- post.Comment(reply);
- break;
- }
- catch (RateLimitException e)
- {
- Console.WriteLine("Rate limited, waiting {0} seconds", Math.Ceiling(e.TimeToReset.TotalSeconds));
- Thread.Sleep(e.TimeToReset);
- Console.WriteLine("Attempting again.");
- }
- }
- return true;
- }
- public static System.Tuple<string, string> GetSauce(string url)
- {
- var request = (HttpWebRequest)WebRequest.Create("https://saucenao.com/search.php?db=999&output_type=2&numres=16&url=" +
- Uri.EscapeUriString(url) + "&api_key=" + Uri.EscapeUriString(Config.SauceNaoAPIKey));
- var response = request.GetResponse();
- var json = JToken.Parse(new StreamReader(response.GetResponseStream()).ReadToEnd());
- response.Close();
- if (json["results"] != null)
- {
- var top = json["results"][0];
- if (top["header"]["similarity"].Value<double>() >= 70 && top["header"]["index_id"].Value<int>() == 5 /* aka Pixiv */)
- return new System.Tuple<string, string>(top["data"]["member_name"].Value<string>(),
- "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=" + top["data"]["pixiv_id"].Value<string>());
- }
- return null;
- }
- public static bool DomainMaps(ref string url)
- {
- var uri = new Uri(url);
- if (uri.Host == "mediacru.sh")
- {
- var request = (HttpWebRequest)WebRequest.Create(url + ".json");
- var response = request.GetResponse();
- var json = JToken.Parse(new StreamReader(response.GetResponseStream()).ReadToEnd());
- if (json["blob_type"].Value<string>() != "image")
- return false;
- url = json["files"].Where(f => f["type"].Value<string>() == "image/png" ||
- f["type"].Value<string>() == "image/jpeg" || f["type"].Value<string>() == "image/svg+xml")
- .Select(f => f["url"].Value<string>()).FirstOrDefault();
- return true;
- }
- return false;
- }
- }
- }