PageRenderTime 22ms CodeModel.GetById 2ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 1ms

/SauceHunt/Program.cs

https://gitlab.com/SirCmpwn/SauceHunt
C# | 291 lines | 283 code | 5 blank | 3 comment | 60 complexity | 505c7b337fab16572dfc77b4fff88cb7 MD5 | raw file
  1using System;
  2using System.IO;
  3using Newtonsoft.Json;
  4using RedditSharp;
  5using System.Threading;
  6using System.Collections.Generic;
  7using System.Linq;
  8using System.Net;
  9using Newtonsoft.Json.Linq;
 10using SauceHunt.Database;
 11using NHibernate.Linq;
 12using Database;
 13
 14namespace SauceHunt
 15{
 16    class MainClass
 17    {
 18        public static Configuration Config;
 19        public static SauceDatabase SauceDatabase;
 20
 21        public static void Main(string[] args)
 22        {
 23            Config = new Configuration();
 24            var configPath = "config.json";
 25            if (args.Length != 0)
 26                configPath = args[0];
 27            if (File.Exists(configPath))
 28                JsonConvert.PopulateObject(File.ReadAllText(configPath), Config);
 29            else
 30            {
 31                File.WriteAllText(configPath, JsonConvert.SerializeObject(Config, Formatting.Indented));
 32                Console.WriteLine(configPath);
 33                return;
 34            }
 35            File.WriteAllText(configPath, JsonConvert.SerializeObject(Config, Formatting.Indented));
 36            if (string.IsNullOrEmpty(Config.PostgreConnectionString))
 37            {
 38                Console.WriteLine("Needs a connection string.");
 39                return;
 40            }
 41            SauceDatabase = new SauceDatabase(Config.PostgreConnectionString);
 42            var reddit = new Reddit();
 43            reddit.LogIn(Config.RedditUsername, Config.RedditPassword);
 44            var subreddits = new List<Subreddit>();
 45            foreach (var sr in Config.Subreddits)
 46                subreddits.Add(reddit.GetSubreddit(sr.Name));
 47            while (true)
 48            {
 49                Console.WriteLine("Running update at {0}", DateTime.Now.ToShortTimeString());
 50                foreach (var _message in reddit.User.GetUnreadMessages())
 51                {
 52                    if (_message is PrivateMessage)
 53                    {
 54                        var message = (PrivateMessage)_message;
 55                        using (var session = SauceDatabase.SessionFactory.OpenSession())
 56                        {
 57                            var user = session.Query<User>().SingleOrDefault(u => u.Name == message.Author);
 58                            if (user == null)
 59                            {
 60                                user = new User
 61                                {
 62                                    Name = message.Author,
 63                                    SkipWaitPeriod = false,
 64                                    IgnoreUser = false,
 65                                    WarnIfNoSauce = false,
 66                                    Posts = new List<RedditPost>()
 67                                };
 68                                using (var transaction = session.BeginTransaction())
 69                                {
 70                                    session.SaveOrUpdate(user);
 71                                    transaction.Commit();
 72                                }
 73                            }
 74                            message.SetAsRead();
 75                            if (message.Body.ToUpper().Contains("RESPOND IMMEDIATELY"))
 76                            {
 77                                user.SkipWaitPeriod = true;
 78                                message.Reply("Okay, from now on, I will find the source for your posts as soon as I see them.");
 79                            }
 80                            else if (message.Body.ToUpper().Contains("SLOW DOWN"))
 81                            {
 82                                user.SkipWaitPeriod = false;
 83                                message.Reply("I'll take things a little slower now.");
 84                            }
 85                            else if (message.Body.ToUpper().Contains("HELP ME FIND SAUCE"))
 86                            {
 87                                user.IgnoreUser = true;
 88                                message.Reply("Sorry, I won't bother you any more.");
 89                            }
 90                            else if (message.Body.ToUpper().Contains("NOTICE ME"))
 91                            {
 92                                user.IgnoreUser = false;
 93                                message.Reply("I'll try to pay more attention to you now.");
 94                            }
 95                            if (message.Body.ToUpper().Contains("WATCH OUT"))
 96                            {
 97                                user.WarnIfNoSauce = true;
 98                                message.Reply("I've got your back, babe.");
 99                            }
100                            if (message.Body.ToUpper().Contains("WARN ME"))
101                            {
102                                user.WarnIfNoSauce = false;
103                                message.Reply("Sorry, I won't look out for you anymore.");
104                            }
105                            using (var transaction = session.BeginTransaction())
106                            {
107                                session.SaveOrUpdate(user);
108                                transaction.Commit();
109                            }
110                        }
111                    }
112                }
113                foreach (var subreddit in subreddits)
114                {
115                    var sr = Config.Subreddits.Single(s => s.Name.ToUpper().Replace("/R/", "") == subreddit.Name.ToUpper());
116                    var posts = subreddit.GetNew().Take(50).ToArray();
117                    foreach (var post in posts)
118                    {
119                        using (var session = SauceDatabase.SessionFactory.OpenSession())
120                        {
121                            if (post.IsSelfPost)
122                                continue;
123                            if (session.Query<RedditPost>().Any(p => p.RedditId == post.Id))
124                                continue;
125                            var user = session.Query<User>().SingleOrDefault(u => u.Name == post.AuthorName);
126                            if (user == null)
127                            {
128                                user = new User
129                                {
130                                    Name = post.AuthorName,
131                                    SkipWaitPeriod = false,
132                                    IgnoreUser = false,
133                                    Posts = new List<RedditPost>()
134                                };
135                                using (var transaction = session.BeginTransaction())
136                                {
137                                    session.Save(user);
138                                    transaction.Commit();
139                                }
140                            }
141                            try
142                            {
143                                var diff = DateTime.Now - post.Created;
144                                Console.WriteLine("Seeing post {1} from {0} minutes ago at {2}", Math.Ceiling(diff.TotalMinutes), post.Id, post.Created.ToString());
145                                bool sauceFound = false;
146                                if (user.SkipWaitPeriod || sr.SauceImmediately || (diff.TotalMinutes > 30 && diff.TotalMinutes < 60))
147                                {
148                                    if (!user.IgnoreUser)
149                                    {
150                                        sauceFound = HandlePost(post);
151                                        if (!sauceFound && user.WarnIfNoSauce)
152                                        {
153                                            reddit.ComposePrivateMessage("Warning: unable to locate sauce", "I was not able to locate sauce for [this post](" + post.Shortlink + ").", post.AuthorName);
154                                        }
155                                        var dbPost = new RedditPost
156                                        {
157                                            SauceFound = sauceFound,
158                                            RedditId = post.Id,
159                                            Submitter = user
160                                        };
161                                        user.Posts.Add(dbPost);
162                                        using (var transaction = session.BeginTransaction())
163                                        {
164                                            session.SaveOrUpdate(dbPost);
165                                            session.SaveOrUpdate(user);
166                                            transaction.Commit();
167                                        }
168                                    }
169                                }
170                            }
171                            catch (Exception e)
172                            {
173                                Console.WriteLine("Error handling {0}", post.Id);
174                                if (!session.Query<RedditPost>().Any(p => p.RedditId == post.Id))
175                                {
176                                    var dbPost = new RedditPost
177                                    {
178                                        SauceFound = false,
179                                        RedditId = post.Id,
180                                        Submitter = user
181                                    };
182                                    user.Posts.Add(dbPost);
183                                    using (var transaction = session.BeginTransaction())
184                                    {
185                                        session.Save(dbPost);
186                                        session.Save(user);
187                                        transaction.Commit();
188                                    }
189                                }
190                            }
191                        }
192                    }
193                }
194                Console.WriteLine("Done.");
195                Thread.Sleep(Config.Interval * 60 * 1000);
196            }
197        }
198
199        public static bool HandlePost(Post post)
200        {
201            // Make sure there's an image there
202            var url = post.Url;
203            var request = (HttpWebRequest)WebRequest.Create(url);
204            request.Method = "HEAD";
205            var response = request.GetResponse();
206            if (!(response.ContentType == "image/png" || response.ContentType == "image/jpeg" || response.ContentType == "image/bmp"))
207            {
208                if (!DomainMaps(ref url))
209                    return false;
210            }
211            // Check to see if someone already posted some sauce
212            var comments = post.GetComments();
213            foreach (var comment in comments)
214            {
215                var text = comment.Body.ToUpper();
216                if (text.Contains("SOURCE") || text.Contains("SAUCE") || text.Contains("ARTIST") ||
217                    text.Contains("PIXIV"))
218                {
219                    return false;
220                }
221            }
222            // I need me some sauce
223            var sauce = GetSauce(url);
224            if (sauce == null)
225                return false;
226            Console.WriteLine("Found sauce for {0}", post.Id);
227            var check = (HttpWebRequest)WebRequest.Create(sauce.Item2);
228            check.Method = "HEAD";
229            try
230            {
231                check.GetResponse();
232            }
233            catch
234            {
235                Console.WriteLine("But it was borked.");
236                return false;
237            }
238            var reply = string.Format(Config.CommentTemplate, sauce.Item1, sauce.Item2);
239            while (true)
240            {
241                try
242                {
243                    post.Comment(reply);
244                    break;
245                }
246                catch (RateLimitException e)
247                {
248                    Console.WriteLine("Rate limited, waiting {0} seconds", Math.Ceiling(e.TimeToReset.TotalSeconds));
249                    Thread.Sleep(e.TimeToReset);
250                    Console.WriteLine("Attempting again.");
251                }
252            }
253            return true;
254        }
255
256        public static System.Tuple<string, string> GetSauce(string url)
257        {
258            var request = (HttpWebRequest)WebRequest.Create("https://saucenao.com/search.php?db=999&output_type=2&numres=16&url=" +
259                Uri.EscapeUriString(url) + "&api_key=" + Uri.EscapeUriString(Config.SauceNaoAPIKey));
260            var response = request.GetResponse();
261            var json = JToken.Parse(new StreamReader(response.GetResponseStream()).ReadToEnd());
262            response.Close();
263            if (json["results"] != null)
264            {
265                var top = json["results"][0];
266                if (top["header"]["similarity"].Value<double>() >= 70 && top["header"]["index_id"].Value<int>() == 5 /* aka Pixiv */)
267                    return new System.Tuple<string, string>(top["data"]["member_name"].Value<string>(),
268                        "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=" + top["data"]["pixiv_id"].Value<string>());
269            }
270            return null;
271        }
272
273        public static bool DomainMaps(ref string url)
274        {
275            var uri = new Uri(url);
276            if (uri.Host == "mediacru.sh")
277            {
278                var request = (HttpWebRequest)WebRequest.Create(url + ".json");
279                var response = request.GetResponse();
280                var json = JToken.Parse(new StreamReader(response.GetResponseStream()).ReadToEnd());
281                if (json["blob_type"].Value<string>() != "image")
282                    return false;
283                url = json["files"].Where(f => f["type"].Value<string>() == "image/png" ||
284                    f["type"].Value<string>() == "image/jpeg" || f["type"].Value<string>() == "image/svg+xml")
285                    .Select(f => f["url"].Value<string>()).FirstOrDefault();
286                return true;
287            }
288            return false;
289        }
290    }
291}