From 39742f4cf4cf383b1f17e5e738295b8788281044 Mon Sep 17 00:00:00 2001 From: lokas Date: Sat, 3 Aug 2024 11:08:20 +0300 Subject: [PATCH 1/8] add base --- tasks/archiving/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tasks/archiving/__init__.py diff --git a/tasks/archiving/__init__.py b/tasks/archiving/__init__.py new file mode 100644 index 00000000..e69de29b From 9447fd9a61936707c4236e6d4285d13bc54a9e42 Mon Sep 17 00:00:00 2001 From: lokas Date: Sat, 3 Aug 2024 11:19:31 +0300 Subject: [PATCH 2/8] add USER_CONFIG --- tasks/archiving/config.py | 8 ++++++++ tasks/archiving/core/__init__.py | 0 2 files changed, 8 insertions(+) create mode 100644 tasks/archiving/config.py create mode 100644 tasks/archiving/core/__init__.py diff --git a/tasks/archiving/config.py b/tasks/archiving/config.py new file mode 100644 index 00000000..39477d2c --- /dev/null +++ b/tasks/archiving/config.py @@ -0,0 +1,8 @@ +from typing import Dict + +USER_CONFIG: Dict[str, str] = { + 'automated_archiving_template' :'أرشفة آلية', + 'setion_type_name':'قسم', + 'setion_type_size':'حجم', + 'skip_template':'رشف' +} diff --git a/tasks/archiving/core/__init__.py b/tasks/archiving/core/__init__.py new file mode 100644 index 00000000..e69de29b From 89f695c6d9a2cce53983a564a846e78c156d1a3f Mon Sep 17 00:00:00 2001 From: lokas Date: Sat, 3 Aug 2024 12:20:45 +0300 Subject: [PATCH 3/8] add main bot --- tasks/archiving/bot.py | 136 ++++++++++++++++++++++++++++++++++++++ tasks/archiving/config.py | 22 ++++-- 2 files changed, 152 insertions(+), 6 deletions(-) create mode 100644 tasks/archiving/bot.py diff --git a/tasks/archiving/bot.py b/tasks/archiving/bot.py new file mode 100644 index 00000000..42839b67 --- /dev/null +++ b/tasks/archiving/bot.py @@ -0,0 +1,136 @@ +import pywikibot +import logging +from abc import ABC, abstractmethod +from typing import List, Dict, Callable +from tasks.archiving.config import USER_CONFIG, TEMPLATE_NAME_KEY + + +# Define the Job interface +class Job(ABC): + @abstractmethod + def perform(self, item): + """ + Perform an action on the given item. + :param item: The item to process, e.g., a page or a file + """ + pass + + +# Implement concrete strategies for different jobs +class ActionJob(Job): + def perform(self, page): + # Implement specific action here + print(f"Performing action on page: {page.title()}") + logging.info(f"Performing action on page: {page.title()}") + + +class LoggingJob(Job): + def perform(self, page): + # Log the page title or other information + print(f"Logging information for page: {page.title()}") + logging.info(f"Logging information for page: {page.title()}") + + +# Define a HookManager for dynamic hooks +class HookManager: + def __init__(self): + self.hooks: Dict[str, List[Callable]] = { + 'before': [], + 'main': [], + 'after': [] + } + + def add_hook(self, point: str, hook: Callable): + if point in self.hooks: + self.hooks[point].append(hook) + + def remove_hook(self, point: str, hook: Callable): + if point in self.hooks: + self.hooks[point].remove(hook) + + def run_hooks(self, point: str, item): + if point in self.hooks: + for hook in self.hooks[point]: + hook(item) + + +# Define the CompositeJob class to handle multiple jobs +class CompositeJob(Job): + def __init__(self, hook_manager: HookManager): + self.jobs: List[Job] = [] + self.hook_manager = hook_manager + + def add_job(self, job: Job): + self.jobs.append(job) + + def perform(self, item): + # Run before hooks + self.hook_manager.run_hooks('before', item) + + # Execute main jobs + for job in self.jobs: + job.perform(item) + + # Run after hooks + self.hook_manager.run_hooks('after', item) + + +# Define the abstract Processor class +class Processor(ABC): + def __init__(self, job: Job): + self.job = job # Dependency injection of Job strategy + + @abstractmethod + def get_items(self): + """ + Retrieve the items to be processed. + :return: A list of items to process + """ + pass + + def process_items(self): + items = self.get_items() + for item in items: + self.job.perform(item) # Delegate action to the injected Job strategy + + +# Concrete implementation for WikiPage processing +class WikiPageProcessor(Processor): + def __init__(self, job: Job): + super().__init__(job) + self.site = pywikibot.Site('ar', 'wikipedia') + self.template_name = USER_CONFIG.get(TEMPLATE_NAME_KEY) + self.template_page = pywikibot.Page(self.site, self.template_name) + + def get_items(self): + pages = self.template_page.embeddedin() + filtered_pages = [ + page for page in pages + if page.depth == 0 and not ('edit' in page.protection() and 'sysop' in page.protection()['edit']) + ] + return filtered_pages + + +# Example hook functions +def before_hook(item): + print(f"Before processing item: {item.title()}") + + +def after_hook(item): + print(f"After processing item: {item.title()}") + + +# Usage +# Create the HookManager +hook_manager = HookManager() +hook_manager.add_hook('before', before_hook) +hook_manager.add_hook('after', after_hook) + +# Create and configure the composite job +composite_job = CompositeJob(hook_manager=hook_manager) +composite_job.add_job(ActionJob()) +composite_job.add_job(LoggingJob()) + +# Create the processor with the composite job +processor = WikiPageProcessor(job=composite_job) +processor.process_items() diff --git a/tasks/archiving/config.py b/tasks/archiving/config.py index 39477d2c..8dd88b54 100644 --- a/tasks/archiving/config.py +++ b/tasks/archiving/config.py @@ -1,8 +1,18 @@ -from typing import Dict +from typing import Dict, Union, List -USER_CONFIG: Dict[str, str] = { - 'automated_archiving_template' :'أرشفة آلية', - 'setion_type_name':'قسم', - 'setion_type_size':'حجم', - 'skip_template':'رشف' +# Define constants for configuration keys +TEMPLATE_NAME_KEY = 'template_name_with_namespace' +ARCHIVING_TEMPLATE_KEY = 'automated_archiving_template' +SECTION_TYPE_KEYS = 'section_type' +SKIP_TEMPLATES_KEY = 'skip_templates' + +# Define the type for configuration values +ConfigValue = Union[str, int, bool, List[str]] # Extend as needed + +# Define the configuration dictionary +USER_CONFIG: Dict[str, ConfigValue] = { + TEMPLATE_NAME_KEY: 'قالب:أرشيف_آلي', + ARCHIVING_TEMPLATE_KEY: 'أرشفة آلية', + SECTION_TYPE_KEYS: ['حجم', 'قسم'], # Example list of section types + SKIP_TEMPLATES_KEY: ['رشف', 'آخر'] # List of skip templates } From 61ac2d5e1c0d9518d849681ed6cc0a174f7d4936 Mon Sep 17 00:00:00 2001 From: lokas Date: Sat, 3 Aug 2024 16:23:19 +0300 Subject: [PATCH 4/8] Archiver class --- tasks/archiving/core/archiver.py | 124 ++++++++++++++++++++++++++++++ tasks/archiving/{ => core}/bot.py | 40 +--------- tasks/archiving/run.py | 35 +++++++++ 3 files changed, 160 insertions(+), 39 deletions(-) create mode 100644 tasks/archiving/core/archiver.py rename tasks/archiving/{ => core}/bot.py (65%) create mode 100644 tasks/archiving/run.py diff --git a/tasks/archiving/core/archiver.py b/tasks/archiving/core/archiver.py new file mode 100644 index 00000000..3c6cb30e --- /dev/null +++ b/tasks/archiving/core/archiver.py @@ -0,0 +1,124 @@ +import re +from datetime import datetime +import wikitextparser as wtp +import pywikibot +import hashlib + +class Section: + def __init__(self, title, content): + self.title = title.strip() + self.content = content + self.id = self._generate_id() + + def _generate_id(self): + content_hash = hashlib.sha1(self.content.encode('utf-8')).hexdigest() + return f"{self.title}_{content_hash}" + + +class Archiver: + def __init__(self, page: pywikibot.Page): + """ + Initializes a Archiver object. + Args: + page (pywikibot.Page): The page to be edited. + """ + # The page to be edited + self.talk_page = page + def archive_talk_page(self, ARCHIVE_THRESHOLD_DAYS=3): + """ + Archives the talk page of the user. + """ + last_comment_timestamps = self.get_last_comment_timestamps() + text = self.talk_page.get() + + sections = self._split_sections(text) + header = self._extract_header(text) + current_time = datetime.utcnow() + archive_text = '' + remaining_text = '' + + for section_title, section_content in sections: + section = Section(section_title, section_content) + + if section.id in last_comment_timestamps: + last_comment_time = last_comment_timestamps[section.id] + if (current_time - last_comment_time).days > ARCHIVE_THRESHOLD_DAYS: + archive_text += section_title + section_content + else: + remaining_text += section_title + section_content + else: + remaining_text += section_title + section_content + + if archive_text: + print("test") + # archive_page = pywikibot.Page(self.site, f'{ARCHIVE_PAGE_PREFIX}{current_time.strftime("%Y-%m")}') + # archive_page.text += archive_text + # archive_page.save(summary='Archiving old discussions') + # + # self.talk_page.text = remaining_text + # self.talk_page.save(summary='Archiving old discussions') + else: + print("No sections to archive.") + + + def get_last_comment_timestamps(self): + history = self.talk_page.revisions(reverse=True, total=500,content=True) # Fetch last 500 revisions + section_last_edit = {} + seen_sections = set() + + for revision in history: + timestamp = revision.timestamp + content = revision.text + + sections = self._split_sections(content) + current_sections = set() + + for section_title, section_content in sections: + section = Section(section_title, section_content) + current_sections.add(section.id) + + if section.id not in section_last_edit: + section_last_edit[section.id] = timestamp + else: + section_last_edit[section.id] = max(section_last_edit[section.id], timestamp) + + removed_sections = seen_sections - current_sections + for section_id in removed_sections: + if section_id not in section_last_edit: + section_last_edit[section_id] = timestamp + + seen_sections = current_sections + + return section_last_edit + def _split_sections(self, text): + parsed = wtp.parse(text) + sections = parsed.sections + # show only sections with level 2 + return [(section.title, section.string) for section in sections if section.level == 2] + def _extract_header(self, text): + parsed = wtp.parse(text) + templates = parsed.templates + + headers = [] + for template in templates: + if template.name == 'رشف': + headers.append(template.span[0]) + headers.append(template.span[1]) + if len(headers) <= 1: + return "" + if len(headers) >= 2: + return text[headers[0]:headers[-1]] + + + +site = pywikibot.Site('ar', 'wikipedia') +page_name = "نقاش_المستخدم:لوقا" +page = pywikibot.Page(site, page_name) +archive_obj = Archiver(page) +archive_obj.archive_talk_page() +""" +read options from template +create class to archive sections +skip sections that have no archive template +customez archive summary +""" \ No newline at end of file diff --git a/tasks/archiving/bot.py b/tasks/archiving/core/bot.py similarity index 65% rename from tasks/archiving/bot.py rename to tasks/archiving/core/bot.py index 42839b67..ee400ae4 100644 --- a/tasks/archiving/bot.py +++ b/tasks/archiving/core/bot.py @@ -24,19 +24,13 @@ def perform(self, page): logging.info(f"Performing action on page: {page.title()}") -class LoggingJob(Job): - def perform(self, page): - # Log the page title or other information - print(f"Logging information for page: {page.title()}") - logging.info(f"Logging information for page: {page.title()}") - # Define a HookManager for dynamic hooks class HookManager: def __init__(self): self.hooks: Dict[str, List[Callable]] = { 'before': [], - 'main': [], + # 'main': [], 'after': [] } @@ -94,23 +88,6 @@ def process_items(self): self.job.perform(item) # Delegate action to the injected Job strategy -# Concrete implementation for WikiPage processing -class WikiPageProcessor(Processor): - def __init__(self, job: Job): - super().__init__(job) - self.site = pywikibot.Site('ar', 'wikipedia') - self.template_name = USER_CONFIG.get(TEMPLATE_NAME_KEY) - self.template_page = pywikibot.Page(self.site, self.template_name) - - def get_items(self): - pages = self.template_page.embeddedin() - filtered_pages = [ - page for page in pages - if page.depth == 0 and not ('edit' in page.protection() and 'sysop' in page.protection()['edit']) - ] - return filtered_pages - - # Example hook functions def before_hook(item): print(f"Before processing item: {item.title()}") @@ -119,18 +96,3 @@ def before_hook(item): def after_hook(item): print(f"After processing item: {item.title()}") - -# Usage -# Create the HookManager -hook_manager = HookManager() -hook_manager.add_hook('before', before_hook) -hook_manager.add_hook('after', after_hook) - -# Create and configure the composite job -composite_job = CompositeJob(hook_manager=hook_manager) -composite_job.add_job(ActionJob()) -composite_job.add_job(LoggingJob()) - -# Create the processor with the composite job -processor = WikiPageProcessor(job=composite_job) -processor.process_items() diff --git a/tasks/archiving/run.py b/tasks/archiving/run.py new file mode 100644 index 00000000..566c1149 --- /dev/null +++ b/tasks/archiving/run.py @@ -0,0 +1,35 @@ +import pywikibot + +from tasks.archiving.config import USER_CONFIG, TEMPLATE_NAME_KEY +from tasks.archiving.core.bot import HookManager, CompositeJob, ActionJob, Processor, Job + + +# Concrete implementation for WikiPage processing +class WikiPageProcessor(Processor): + def __init__(self, job: Job): + super().__init__(job) + self.site = pywikibot.Site('ar', 'wikipedia') + self.template_name = USER_CONFIG.get(TEMPLATE_NAME_KEY) + self.template_page = pywikibot.Page(self.site, self.template_name) + + def get_items(self): + pages = self.template_page.embeddedin() + filtered_pages = [ + page for page in pages + if page.depth == 0 and not ('edit' in page.protection() and 'sysop' in page.protection()['edit']) + ] + return filtered_pages + + +# Create the HookManager +hook_manager = HookManager() +# hook_manager.add_hook('before', before_hook) +# hook_manager.add_hook('after', after_hook) + +# Create and configure the composite job +composite_job = CompositeJob(hook_manager=hook_manager) +composite_job.add_job(ActionJob()) + +# Create the processor with the composite job +processor = WikiPageProcessor(job=composite_job) +processor.process_items() From a54c55872d19b2860e5b47725c828221de53f96d Mon Sep 17 00:00:00 2001 From: lokas Date: Sat, 3 Aug 2024 17:55:52 +0300 Subject: [PATCH 5/8] Options --- tasks/archiving/core/archiver.py | 63 +++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/tasks/archiving/core/archiver.py b/tasks/archiving/core/archiver.py index 3c6cb30e..3032ec77 100644 --- a/tasks/archiving/core/archiver.py +++ b/tasks/archiving/core/archiver.py @@ -4,6 +4,57 @@ import pywikibot import hashlib + +class Options: + def __init__(self, page: pywikibot.Page, template_name: str = "أرشفة آلية"): + """ + Initializes the object with the given `page` and `template_name`. + + Parameters: + page (pywikibot.Page): The page object. + template_name (str, optional): The name of the template. Defaults to "أرشفة آلية". + + Returns: + None + """ + self.template_name = template_name + self.page = page + self.option = ('قسم', '3', None) + self._get_params() + + def _get_template(self): + """ + Retrieves the template with the specified name from the page's wikitext. + + Returns: + wtp.Template or None: The template object if found, None otherwise. + """ + text = self.page.get() + templates = wtp.parse(text).templates + for t in templates: + if t.name == self.template_name: + return t + return None + + def _get_params(self): + """ + Retrieves the parameters from the template. + + Returns: + tuple or None: A tuple containing the values of the template arguments if the template has exactly three arguments, + or None if the template is not found or has a different number of arguments. + """ + template = self._get_template() + if template is None: + return None + + arguments = template.arguments + if len(arguments) == 3: + self.option = (arguments[0].value, arguments[1].value, arguments[2].value) + + + + class Section: def __init__(self, title, content): self.title = title.strip() @@ -24,6 +75,8 @@ def __init__(self, page: pywikibot.Page): """ # The page to be edited self.talk_page = page + self.options = (Options(self.talk_page)).option + def archive_talk_page(self, ARCHIVE_THRESHOLD_DAYS=3): """ Archives the talk page of the user. @@ -60,9 +113,8 @@ def archive_talk_page(self, ARCHIVE_THRESHOLD_DAYS=3): else: print("No sections to archive.") - def get_last_comment_timestamps(self): - history = self.talk_page.revisions(reverse=True, total=500,content=True) # Fetch last 500 revisions + history = self.talk_page.revisions(reverse=True, total=500, content=True) # Fetch last 500 revisions section_last_edit = {} seen_sections = set() @@ -90,11 +142,13 @@ def get_last_comment_timestamps(self): seen_sections = current_sections return section_last_edit + def _split_sections(self, text): parsed = wtp.parse(text) sections = parsed.sections # show only sections with level 2 return [(section.title, section.string) for section in sections if section.level == 2] + def _extract_header(self, text): parsed = wtp.parse(text) templates = parsed.templates @@ -105,12 +159,11 @@ def _extract_header(self, text): headers.append(template.span[0]) headers.append(template.span[1]) if len(headers) <= 1: - return "" + return "" if len(headers) >= 2: return text[headers[0]:headers[-1]] - site = pywikibot.Site('ar', 'wikipedia') page_name = "نقاش_المستخدم:لوقا" page = pywikibot.Page(site, page_name) @@ -121,4 +174,4 @@ def _extract_header(self, text): create class to archive sections skip sections that have no archive template customez archive summary -""" \ No newline at end of file +""" From 63b17e8ed64754c7000bb025223f993e021c2a5c Mon Sep 17 00:00:00 2001 From: lokas Date: Sat, 3 Aug 2024 18:04:50 +0300 Subject: [PATCH 6/8] add skip template --- tasks/archiving/core/archiver.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tasks/archiving/core/archiver.py b/tasks/archiving/core/archiver.py index 3032ec77..8cd7cbdc 100644 --- a/tasks/archiving/core/archiver.py +++ b/tasks/archiving/core/archiver.py @@ -3,6 +3,7 @@ import wikitextparser as wtp import pywikibot import hashlib +from core.utils.helpers import prepare_str class Options: @@ -54,17 +55,23 @@ def _get_params(self): - class Section: def __init__(self, title, content): self.title = title.strip() self.content = content self.id = self._generate_id() - + self.skip = False + self.skip_templates = [prepare_str("لا للأرشفة")] + self._skip() def _generate_id(self): content_hash = hashlib.sha1(self.content.encode('utf-8')).hexdigest() return f"{self.title}_{content_hash}" - + def _skip(self): + parse = wtp.parse(self.content) + for template in parse.templates: + if prepare_str(template.normal_name()) in self.skip_templates: + self.skip = True + break class Archiver: def __init__(self, page: pywikibot.Page): @@ -93,6 +100,10 @@ def archive_talk_page(self, ARCHIVE_THRESHOLD_DAYS=3): for section_title, section_content in sections: section = Section(section_title, section_content) + if section.skip: + remaining_text += section_title + section_content + continue + if section.id in last_comment_timestamps: last_comment_time = last_comment_timestamps[section.id] if (current_time - last_comment_time).days > ARCHIVE_THRESHOLD_DAYS: @@ -170,8 +181,6 @@ def _extract_header(self, text): archive_obj = Archiver(page) archive_obj.archive_talk_page() """ -read options from template create class to archive sections -skip sections that have no archive template customez archive summary """ From dcf983c095d78e1109693c293f330899b3a9de83 Mon Sep 17 00:00:00 2001 From: lokas Date: Sat, 3 Aug 2024 19:23:08 +0300 Subject: [PATCH 7/8] some fix --- tasks/archiving/core/archiver.py | 68 ++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/tasks/archiving/core/archiver.py b/tasks/archiving/core/archiver.py index 8cd7cbdc..6e600c53 100644 --- a/tasks/archiving/core/archiver.py +++ b/tasks/archiving/core/archiver.py @@ -64,8 +64,8 @@ def __init__(self, title, content): self.skip_templates = [prepare_str("لا للأرشفة")] self._skip() def _generate_id(self): - content_hash = hashlib.sha1(self.content.encode('utf-8')).hexdigest() - return f"{self.title}_{content_hash}" + content_hash = hashlib.sha1(self.content.encode('utf-8', 'ignore')).hexdigest().encode('utf-8', 'ignore') + return f"{prepare_str(self.title)}_{content_hash}" def _skip(self): parse = wtp.parse(self.content) for template in parse.templates: @@ -84,19 +84,22 @@ def __init__(self, page: pywikibot.Page): self.talk_page = page self.options = (Options(self.talk_page)).option - def archive_talk_page(self, ARCHIVE_THRESHOLD_DAYS=3): + def archive_talk_page(self): """ Archives the talk page of the user. """ - last_comment_timestamps = self.get_last_comment_timestamps() text = self.talk_page.get() - - sections = self._split_sections(text) header = self._extract_header(text) current_time = datetime.utcnow() archive_text = '' remaining_text = '' + sections = self._split_sections(text) + + last_comment_timestamps = self.get_last_comment_timestamps() + + + for section_title, section_content in sections: section = Section(section_title, section_content) @@ -106,13 +109,17 @@ def archive_talk_page(self, ARCHIVE_THRESHOLD_DAYS=3): if section.id in last_comment_timestamps: last_comment_time = last_comment_timestamps[section.id] - if (current_time - last_comment_time).days > ARCHIVE_THRESHOLD_DAYS: + if (current_time - last_comment_time).days > int(self.options[1]): archive_text += section_title + section_content else: remaining_text += section_title + section_content else: remaining_text += section_title + section_content + if self.options[0] != 'قسم': + if len(self.talk_page.text) < int(self.options[1]) * 1000: + archive_text = '' + if archive_text: print("test") # archive_page = pywikibot.Page(self.site, f'{ARCHIVE_PAGE_PREFIX}{current_time.strftime("%Y-%m")}') @@ -125,32 +132,35 @@ def archive_talk_page(self, ARCHIVE_THRESHOLD_DAYS=3): print("No sections to archive.") def get_last_comment_timestamps(self): - history = self.talk_page.revisions(reverse=True, total=500, content=True) # Fetch last 500 revisions + history = self.talk_page.revisions(reverse=False, total=500, content=True) # Fetch last 500 revisions section_last_edit = {} seen_sections = set() for revision in history: - timestamp = revision.timestamp - content = revision.text - - sections = self._split_sections(content) - current_sections = set() - - for section_title, section_content in sections: - section = Section(section_title, section_content) - current_sections.add(section.id) - - if section.id not in section_last_edit: - section_last_edit[section.id] = timestamp - else: - section_last_edit[section.id] = max(section_last_edit[section.id], timestamp) - - removed_sections = seen_sections - current_sections - for section_id in removed_sections: - if section_id not in section_last_edit: - section_last_edit[section_id] = timestamp - - seen_sections = current_sections + try: + timestamp = revision.timestamp + content = revision.text + + sections = self._split_sections(content) + current_sections = set() + + for section_title, section_content in sections: + section = Section(section_title, section_content) + current_sections.add(section.id) + + if section.id not in section_last_edit: + section_last_edit[section.id] = timestamp + else: + section_last_edit[section.id] = max(section_last_edit[section.id], timestamp) + + removed_sections = seen_sections - current_sections + for section_id in removed_sections: + if section_id not in section_last_edit: + section_last_edit[section_id] = timestamp + + seen_sections = current_sections + except Exception as e: + print(f"Error processing revision {revision.revid}: {e}") return section_last_edit From 0783a5fc579961e060bc8a8516a45e5217eb8a0a Mon Sep 17 00:00:00 2001 From: lokas Date: Sat, 3 Aug 2024 19:27:44 +0300 Subject: [PATCH 8/8] use min --- tasks/archiving/core/archiver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/archiving/core/archiver.py b/tasks/archiving/core/archiver.py index 6e600c53..3f686e3b 100644 --- a/tasks/archiving/core/archiver.py +++ b/tasks/archiving/core/archiver.py @@ -132,7 +132,7 @@ def archive_talk_page(self): print("No sections to archive.") def get_last_comment_timestamps(self): - history = self.talk_page.revisions(reverse=False, total=500, content=True) # Fetch last 500 revisions + history = self.talk_page.revisions(reverse=False, total=20, content=True) # Fetch last 500 revisions section_last_edit = {} seen_sections = set() @@ -151,7 +151,7 @@ def get_last_comment_timestamps(self): if section.id not in section_last_edit: section_last_edit[section.id] = timestamp else: - section_last_edit[section.id] = max(section_last_edit[section.id], timestamp) + section_last_edit[section.id] = min(section_last_edit[section.id], timestamp) removed_sections = seen_sections - current_sections for section_id in removed_sections: