User:B-bot/source/OTRS Dated Filer

This task will create monthly categories for {{OTRS pending}} and {{OTRS received}}. It will check for templates missing a date and add it to them.

    public class OtrsDatedFiler : BBotBase
    {
        /// <summary>
        /// Gets the name for this job
        /// </summary>
        /// <returns></returns>
        public override string GetJobName()
        {
            return "OTRS dated filer";
        }

        /// <summary>
        /// Both tasks will do this many.  (If you want 50 edits, make this 25.)
        /// </summary>
        public int MaximumImagesToProcess = 1000;

        /// <summary>
        /// This function creates the requested category if it does not already exist
        /// </summary>
        /// <param name="CategoryName"></param>
        /// <param name="StarterTemplate"></param>
        /// <returns></returns>
        public bool CreateCatIfNeeded(ref Site site, Page pgUserspaceTest, String CategoryName, String StarterTemplate)
        {
            try
            {
                // Make sure that this month's and next month's dated categories exist
                Page cat = new Page(site, CategoryName);
                cat.Load();

                SleepApiDelay();

                if (!cat.Exists())
                {
                    cat.text = StarterTemplate;

                    if (UserspaceTest)
                    {
                        pgUserspaceTest.text += "|-\r\n| [[:" + cat.title + "]] || ~~~~~ || <pre>" + cat.text.Substring(0, Math.Min(300, cat.text.Length)) + "</pre>\r\n";
                        pgUserspaceTest.Save("Creating monthly category", false);
                    }
                    else
                    {
                        cat.Save(cat.text, "Creating monthly category", false);
                    }

                    SleepApiDelay();

                    LogToEventLog(ref site, MessageType.Informational, "Created category [[:" + CategoryName + "]] using <code><nowiki>" + StarterTemplate + "</nowiki></code>.", null);
                }

                SleepApiDelay();

                return true;
            }
            catch (Exception ex)
            {
                LogToEventLog(ref site, MessageType.Informational, "Error creating category [[:" + CategoryName + "]] using <code><nowiki>" + StarterTemplate + "</nowiki></code>.", ex);
                return false;
            }
        }

        /// <summary>
        /// This function will add the date to the requested 
        /// </summary>
        /// <param name="site"></param>
        /// <param name="pg"></param>
        /// <param name="strRegex"></param>
        /// <param name="strTagName">"OTRS Pending", "OTRS received", etc</param>
        /// <param name="pgUserspaceTest">If this is a userspace test, the page on which that is being conducted</param>
        /// <param name="strCategoryFormatString">Format string to be used if we need to create the category</param>
        /// <param name="StarterTemplate">Starter template, if we need to create the category</param>
        /// <returns>True if the tag was modified, false otherwise</returns>
        public bool DateTag(Site site, Page pg, String strRegex, String strTagName, Page pgUserspaceTest, String strCategoryFormatString, String StarterTemplate)
        {
            try
            {
                // If this is a user talk base page, then {{OTRS pending}} has probably been added to the wrong place
                if (pg.title.ToLower().StartsWith("user talk:") && !pg.title.Contains("/"))
                {
                    LogToEventLog(ref site, MessageType.Error, "Error: [[" + pg.title + "]] has a {{tl|" + strTagName + "}} tag.  " +
                                                               "This tag should only be placed on file pages or article talk pages (including talk pages of draft articles).  " +
                                                               "It should not be placed directly on user talk pages.", null);

                    return false;
                }

                pg.Load();

                // Find the OTRS pending tag
                Match match = Regex.Match(pg.text, strRegex, RegexOptions.IgnoreCase);
                if (null != match && 0 < match.Length)
                {
                    // Remove the tag from the string
                    String strNewText = "";
                    if (0 < match.Index)
                    {
                        strNewText = pg.text.Substring(0, match.Index);
                    }

                    // This is the tag, without the close
                    String strTag = pg.text.Substring(match.Index, match.Length).Replace("}}", "");

                    // If the tag appears to already have a date, then move on
                    if (strTag.Contains("date") ||
                        strTag.Contains("year") ||
                        strTag.Contains("month") ||
                        strTag.Contains("day"))
                    {
                        LogToEventLog(ref site, MessageType.Error, "Wanted to add a date to the " + strTagName + " tag of [[:" + pg.title + "]], but it seems to already have one.  Maybe the page has multiple tags?", null);
                        return false;
                    }

                    // Grab the timestamp (it gets blown away once we save)
                    DateTime dtmTimestamp = pg.timestamp;

                    // Add the date
                    strTag += "|year=" + dtmTimestamp.Year.ToString() +
                              "|month=" + dtmTimestamp.Month.ToString("00") +
                              "|day=" + dtmTimestamp.Day.ToString("00") + "}}";

                    strNewText += strTag;

                    // Add everything after the tag
                    if (match.Index + match.Length < pg.text.Length - 1)
                    {
                        strNewText += pg.text.Substring(match.Index + match.Length);
                    }

                    // Trim whitespace
                    strNewText = strNewText.Trim();

                    String strEditSummary = "Adding date to [[Template:" + strTagName + "|" + strTagName + "]] tag.";

                    if (UserspaceTest)
                    {
                        pgUserspaceTest.text += "|-\r\n| [[:" + pg.title + "]] || ~~~~~ || <pre>" +
                                                pg.text.Substring(0, Math.Min(1500, pg.text.Length)) +
                                                "</pre> || <pre>" + strNewText.Substring(0, Math.Min(1500, strNewText.Length)) + "</pre>\r\n";
                        pgUserspaceTest.Save(strEditSummary, false);
                    }
                    else
                    {
                        pg.Save(strNewText, strEditSummary, false);
                    }

                    // Now, if that tag is more than a month ago, we need to check the category name to see if it exists
                    if ((DateTime.Now - dtmTimestamp).TotalDays > 30)
                    {
                        CreateCatIfNeeded(ref site, pgUserspaceTest, 
                                          String.Format(strCategoryFormatString, dtmTimestamp), 
                                          String.Format(StarterTemplate, dtmTimestamp));
                    }

                    return true;
                }
                else
                {
                    LogToEventLog(ref site, MessageType.Error, "Error: though [[:" + pg.title + "]] is categorized as having an undated {{tl|" + strTagName + "}} tag, the tag could not be located.", null);
                }
            }
            catch (Exception ex)
            {
                LogToEventLog(ref site, MessageType.Error, "Error dating " + strTagName + " tag for page [[:" + pg.title + "]].", ex);
            }

            return false;
        }

        /// <summary>
        /// This function will perform the task
        /// </summary>
        public void PerformTask()
        {
            // OTRS pending categories
            const String csOtrsPendingUnknownDateCategoryName = "Category:Items pending OTRS confirmation of permission as of unknown date";
            const String csDatedOtrsPendingCategoryFormatString = "Category:Items pending OTRS confirmation of permission as of {0:MMMM yyyy}";
            String strDatedOtrsPendingCategoryThisMonth = String.Format(csDatedOtrsPendingCategoryFormatString, DateTime.Now);
            String strDatedOtrsPendingCategoryNextMonth = String.Format(csDatedOtrsPendingCategoryFormatString, DateTime.Now.AddMonths(1));
            const String csDatedOtrsPendingCategoryStarterString = "{{{{subst:OTRS pending subcat starter|date={0:yyyy-MM-dd}}}}}";
            String strDatedOtrsPendingCategoryStarterThisMonth = String.Format(csDatedOtrsPendingCategoryStarterString, DateTime.Parse(DateTime.Now.AddMonths(1).ToString("MM-01-yyyy")).AddDays(-1));
            String strDatedOtrsPendingCategoryStarterNextMonth = String.Format(csDatedOtrsPendingCategoryStarterString, DateTime.Parse(DateTime.Now.AddMonths(2).ToString("MM-01-yyyy")).AddDays(-1));

            // OTRS received categories (files)
            const String csOtrsReceivedFilesUnknownDateCategoryName = "Category:Wikipedia files with unconfirmed permission received by OTRS as of unknown date";
            const String csDatedOtrsReceivedFilesCategoryFormatString = "Category:Wikipedia files with unconfirmed permission received by OTRS as of {0:MMMM yyyy}";
            String strDatedOtrsReceivedFilesCategoryThisMonth = String.Format(csDatedOtrsReceivedFilesCategoryFormatString, DateTime.Now);
            String strDatedOtrsReceivedFilesCategoryNextMonth = String.Format(csDatedOtrsReceivedFilesCategoryFormatString, DateTime.Now.AddMonths(1));
            const String csDatedOtrsReceivedFilesCategoryStarterString = "{{{{subst:OTRS received subcat starter|date={0:yyyy-MM-dd}|type=files}}}}";
            String strDatedOtrsReceivedFilesCategoryStarterThisMonth = String.Format(csDatedOtrsReceivedFilesCategoryStarterString, DateTime.Parse(DateTime.Now.AddMonths(1).ToString("MM-01-yyyy")).AddDays(-1));
            String strDatedOtrsReceivedFilesCategoryStarterNextMonth = String.Format(csDatedOtrsReceivedFilesCategoryStarterString, DateTime.Parse(DateTime.Now.AddMonths(2).ToString("MM-01-yyyy")).AddDays(-1));

            // OTRS received categories (pages)
            const String csOtrsReceivedPagesUnknownDateCategoryName = "Category:Wikipedia pages with unconfirmed permission received by OTRS as of unknown date";
            const String csDatedOtrsReceivedPagesCategoryFormatString = "Category:Wikipedia pages with unconfirmed permission received by OTRS as of {0:MMMM yyyy}";
            String strDatedOtrsReceivedPagesCategoryThisMonth = String.Format(csDatedOtrsReceivedPagesCategoryFormatString, DateTime.Now);
            String strDatedOtrsReceivedPagesCategoryNextMonth = String.Format(csDatedOtrsReceivedPagesCategoryFormatString, DateTime.Now.AddMonths(1));
            const String csDatedOtrsReceivedPagesCategoryStarterString = "{{{{subst:OTRS received subcat starter|date={0:yyyy-MM-dd}|type=pages}}}}";
            String strDatedOtrsReceivedPagesCategoryStarterThisMonth = String.Format(csDatedOtrsReceivedPagesCategoryStarterString, DateTime.Parse(DateTime.Now.AddMonths(1).ToString("MM-01-yyyy")).AddDays(-1));
            String strDatedOtrsReceivedPagesCategoryStarterNextMonth = String.Format(csDatedOtrsReceivedPagesCategoryStarterString, DateTime.Parse(DateTime.Now.AddMonths(2).ToString("MM-01-yyyy")).AddDays(-1));


            // Connect to Wikipedia
            Site site = TryToConnect("https://en.wikipedia.org", Properties.Settings.Default.BotUserName, Properties.Settings.Default.BotPassword);

            // Use a separate connection for our API calls - this seems to time out less frequently
            Site site2 = TryToConnect("https://en.wikipedia.org", Properties.Settings.Default.BotUserName, Properties.Settings.Default.BotPassword);

            // Grab the list of pages in the category
            PageList pl = new PageList(site);
            pl.FillAllFromCategory(csOtrsPendingUnknownDateCategoryName);

            SleepApiDelay();

            if (UserspaceTest)
            {
                LogToEventLog(ref site, MessageType.Start, "B-Bot \"OTRS Dated Filer\" process now commencing <font color='red'>'''IN TEST MODE'''</font>.  This process checks pages from [[:"
                                                           + csOtrsPendingUnknownDateCategoryName + "]] to add the date stamp where it is missing.", null);
            }
            else
            {
                LogToEventLog(ref site, MessageType.Start, "B-Bot \"OTRS Dated Filer\" process now commencing.  This process checks pages from [[:"
                                                           + csOtrsPendingUnknownDateCategoryName + "]] to add the date stamp where it is missing.", null);
            }

            int intPagesTagged = 0;

            Page pgUserspaceTest = new Page(site, Properties.Settings.Default.UserspaceTestPage);

            // Add the userspace test header
            if (UserspaceTest)
            {
                pgUserspaceTest.text = "Now beginning OTRS Dated Filer task on " + DateTime.Now.ToString() + " (local time) ...\r\n\r\n";
                pgUserspaceTest.text += "{| class=\"wikitable sortable\"\r\n|-\r\n! Page !! Timestamp !! Former text !! Proposed text\r\n";
                pgUserspaceTest.Save();
            }

            SleepApiDelay();

            // Make sure that this month's and next month's dated categories exist
            CreateCatIfNeeded(ref site, pgUserspaceTest, strDatedOtrsPendingCategoryThisMonth, strDatedOtrsPendingCategoryStarterThisMonth);
            SleepApiDelay();
            CreateCatIfNeeded(ref site, pgUserspaceTest, strDatedOtrsPendingCategoryNextMonth, strDatedOtrsPendingCategoryStarterNextMonth);

            SleepApiDelay();

            // Loop through each page
            foreach (Page pgCurrentImagePage in pl)
            {
                // Ignore things that are not in a valid namespace
                if (pgCurrentImagePage.GetNamespace() != 6 && // file
                    pgCurrentImagePage.GetNamespace() != 119 && // draft talk
                    pgCurrentImagePage.GetNamespace() != 118 && // draft
                    pgCurrentImagePage.GetNamespace() != 3 && // user talk
                    pgCurrentImagePage.GetNamespace() != 5) // WP: talk
                {
                    continue;
                }

                // Find the OTRS pending tag
                DateTag(site, pgCurrentImagePage, @"\{\{\s*(template\:|)otrs(\s|-|)pending([^\{^\}]*|)\}\}", "OTRS pending", pgUserspaceTest, csDatedOtrsPendingCategoryFormatString, csDatedOtrsPendingCategoryStarterString);

                SleepApiDelay();

                if (BotStop(site))
                {
                    LogToEventLog(ref site, MessageType.Error, "I was ordered to abort.", null);
                    return;
                }

                intPagesTagged++;

                SleepApiDelay();

                if (intPagesTagged >= MaximumImagesToProcess)
                {
                    break;
                }
            }

            LogToEventLog(ref site2, MessageType.Informational, "B-Bot finished adding OTRS pending dates.  " + intPagesTagged.ToString() + " pages were edited.  Will now check category [[:" + csOtrsReceivedFilesUnknownDateCategoryName + "]]", null);

            // Now, do OTRS received (files)

            // Grab the list of pages in the category
            pl = new PageList(site);
            pl.FillAllFromCategory(csOtrsReceivedFilesUnknownDateCategoryName);

            SleepApiDelay();

            intPagesTagged = 0;

            SleepApiDelay();

            // Make sure that this month's and next month's dated categories exist
            CreateCatIfNeeded(ref site, pgUserspaceTest, strDatedOtrsReceivedFilesCategoryThisMonth, strDatedOtrsReceivedFilesCategoryStarterThisMonth);
            SleepApiDelay();
            CreateCatIfNeeded(ref site, pgUserspaceTest, strDatedOtrsReceivedFilesCategoryNextMonth, strDatedOtrsReceivedFilesCategoryStarterNextMonth);

            SleepApiDelay();

            // Loop through each page
            foreach (Page pgCurrentImagePage in pl)
            {
                // Find the OTRS pending tag
                DateTag(site, pgCurrentImagePage, @"\{\{\s*(template\:|)otrs(\s|-|)(received|insufficient)([^\{^\}]*|)\}\}", "OTRS received", pgUserspaceTest, csDatedOtrsReceivedFilesCategoryFormatString, csDatedOtrsReceivedFilesCategoryStarterString);

                SleepApiDelay();

                if (BotStop(site))
                {
                    LogToEventLog(ref site, MessageType.Error, "I was ordered to abort.", null);
                    return;
                }

                intPagesTagged++;

                SleepApiDelay();

                if (intPagesTagged >= MaximumImagesToProcess)
                {
                    break;
                }
            }

            LogToEventLog(ref site2, MessageType.Informational, "B-Bot finished adding OTRS received dates to files.  " + intPagesTagged.ToString() + " files were edited.  Will now check category [[:" + csOtrsReceivedPagesUnknownDateCategoryName + "]]", null);

            intPagesTagged = 0;

            // Now OTRS received pages

            // Grab the list of pages in the category
            pl = new PageList(site);
            pl.FillAllFromCategory(csOtrsReceivedPagesUnknownDateCategoryName);

            SleepApiDelay();

            // Make sure that this month's and next month's dated categories exist
            CreateCatIfNeeded(ref site, pgUserspaceTest, strDatedOtrsReceivedPagesCategoryThisMonth, strDatedOtrsReceivedPagesCategoryStarterThisMonth);
            SleepApiDelay();
            CreateCatIfNeeded(ref site, pgUserspaceTest, strDatedOtrsReceivedPagesCategoryNextMonth, strDatedOtrsReceivedPagesCategoryStarterNextMonth);

            SleepApiDelay();

            // Loop through each page
            foreach (Page pgCurrentImagePage in pl)
            {
                // Find the OTRS pending tag
                DateTag(site, pgCurrentImagePage, @"\{\{\s*(template\:|)otrs(\s|-|)(received|insufficient)([^\{^\}]*|)\}\}", "OTRS received", pgUserspaceTest, csDatedOtrsReceivedPagesCategoryFormatString, csDatedOtrsReceivedPagesCategoryStarterString);

                SleepApiDelay();

                if (BotStop(site))
                {
                    LogToEventLog(ref site, MessageType.Error, "I was ordered to abort.", null);
                    return;
                }

                intPagesTagged++;

                SleepApiDelay();

                if (intPagesTagged >= MaximumImagesToProcess)
                {
                    break;
                }
            }

            LogToEventLog(ref site2, MessageType.Informational, "B-Bot finished adding OTRS received dates to pages.  " + intPagesTagged.ToString() + " pages were edited.", null);

            LogToEventLog(ref site2, MessageType.Finish, "B-Bot OTRS Dated Filer process completed.", null);
            if (UserspaceTest)
            {
                pgUserspaceTest.text += "|}\r\n";
                pgUserspaceTest.Save();
            }
        }