1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
"""
1. Download ChromeDriver: https://sites.google.com/a/chromium.org/chromedriver/home
2. Put the executable into your PATH.
3. Install selenium into Python environment.
4. Python plurk_response_deleter.py

Update:
    2020/4/9: * 更新自動點擊載入舊回覆,可以刪除回覆過多的噗
              * 修正如果有新回覆的噗會被跳過的問題
"""
import random
import logging
import time

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import ElementClickInterceptedException
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.action_chains import ActionChains

logger = logging.getLogger('plurk_deleter')


def random_wait(secs=1.0):
    t = secs + random.random() * secs
    time.sleep(t)


class ChromeDriver(object):
    def __init__(self, user_data_dir='default_user'):
        self.user_data_dir = user_data_dir
        self.driver = None
        self.reset()

    def __del__(self):
        if self.driver is not None:
            self.driver.quit()

    def get(self, url):
        self.driver.get(url)

    def reset(self):
        if self.driver is not None:
            self.driver.quit()
            self.driver = None
        chrome_options = Options()
        chrome_options.add_argument('user-data-dir=%s' % self.user_data_dir)
        self.driver = webdriver.Chrome(chrome_options=chrome_options)

    def switch_to_window(self, window_idx=0):
        self.driver.switch_to.window(self.driver.window_handles[window_idx])

    def close(self):
        self.driver.close()

    def refresh(self):
        self.driver.refresh()


class PlurkChromeDriver(ChromeDriver):
    def main_loop(self):
        self._get_to_replies()
        while True:
            random_wait(1)
            deleted = set()
            try_laters = []
            for next_plurk in self._get_all_plurks(skip_authored=True,
                                                   only_偷偷說=True):
                next_try_laters = []
                for plurk in [next_plurk] + try_laters:
                    plurk_id = plurk.get_attribute('id')
                    if plurk.location['x'] < 0:
                        logger.warning('plurk is invisible, skip it')
                        continue
                    if plurk_id not in deleted:
                        logger.warning('discover new plurk: %s', plurk.text)
                        try:
                            self._delete_偷偷說_plurk_responses(plurk)
                            deleted.add(plurk_id)
                        except ElementClickInterceptedException as e:
                            logger.warning('plurk is hidden, try later: %s', e)
                            next_try_laters.append(plurk)
                        except Exception as e:
                            logger.warning('delete failed, try later: %s', e)
                            next_try_laters.append(plurk)
                try_laters = next_try_laters
            self._move_right()

    def _move_right(self):
        button = self.driver.find_element_by_css_selector(
            '.cmp_arrow_right.pif-arrow-right')
        ActionChains(self.driver).move_to_element(button).click().perform()
        random_wait(1)

    def _delete_偷偷說_plurk_responses(self, root):
        # note this only supports 偷偷說

        # remove previous focus
        button = self.driver.find_element_by_css_selector(
            '.cmp_arrow_right.pif-arrow-right')
        ActionChains(self.driver).move_to_element(button).perform()

        # refresh element
        root_id = root.get_attribute('id')
        root = self.driver.find_element_by_id(root_id)

        # open the plurk
        ActionChains(self.driver).move_to_element(root).perform()
        button = root.find_element_by_css_selector('.qualifier.q_whispers')
        button.click()
        random_wait(5)

        response_box = self.driver.find_element_by_class_name('response_box')
        self._load_older_if_exist(response_box)
        for response in response_box.find_elements_by_css_selector(
                '.list .plurk.cboxAnchor.response'):
            ActionChains(self.driver).move_to_element(response).perform()
            name = response.find_element_by_css_selector('td.td_qual span a ')
            style = name.get_attribute('style')
            if '#0a9c17' not in style and 'rgb(10, 156, 23)' not in style:
                continue
            random_wait(1)
            logger.warning('delete response: %s', response.text)
            button = response.find_element_by_css_selector(
                '.response-manager .pif-option')
            ActionChains(self.driver).move_to_element(button).click().perform()
            random_wait(2)
            button = self.driver.find_element_by_css_selector(
                '.pop-view-content .pif-delete')
            ActionChains(self.driver).move_to_element(button).click().perform()
            random_wait(1)
            self.driver.switch_to.alert.accept()
            random_wait(1)

        # refresh element
        root_id = root.get_attribute('id')
        root = self.driver.find_element_by_id(root_id)
        button = root.find_element_by_css_selector('.qualifier.q_whispers')
        # close the plurk
        button.click()
        random_wait(1)

    def _load_older_if_exist(self, response_box):
        try:
            while True:
                load_older = response_box.find_element_by_class_name(
                    'load-older-holder')
                if load_older.is_displayed():
                    for response in response_box.find_elements_by_css_selector(
                            '.list .plurk.cboxAnchor.response'):
                        ActionChains(
                            self.driver).move_to_element(response).perform()
                        break
                    button = load_older.find_element_by_class_name('button')
                    ActionChains(self.driver).move_to_element(button).perform()
                    random_wait(1)
                    ActionChains(
                        self.driver).move_to_element(button).click().perform()
                    random_wait(2)
                else:
                    break
        except NoSuchElementException:
            pass

    def _get_all_plurks(self,
                        skip_authored=True,
                        only_偷偷說=True,
                        only_current=True):
        for element in self.driver.find_elements_by_css_selector(
                'div.plurk.cboxAnchor'):
            # skip past plurks
            if only_current and element.location['x'] < 0:
                continue
            if skip_authored:
                if element.find_elements_by_class_name('pif-edit'):
                    logger.warning('skip authored: %s', element.text)
                    continue
            if only_偷偷說:
                if not element.find_elements_by_class_name('q_whispers'):
                    continue
            yield element

    def _get_to_replies(self):
        self.get('https://plurk.com')
        random_wait(2)
        button = self.driver.find_element_by_id('all_plurks')
        ActionChains(self.driver).move_to_element(button).perform()
        random_wait(2)
        button = self.driver.find_element_by_id('responded_plurks_tab_btn')
        ActionChains(self.driver).move_to_element(button).click().perform()


def main():
    driver = PlurkChromeDriver()
    input('Please login to Plurk, and then click Enter:')
    driver.main_loop()


if __name__ == '__main__':
    main()