Menü×

Tauziehen. [BEENDET]

  • #4561 WD_40 21 Feb. 2023, 18:04

    Nächstes Tauziehen: User mit x oder y im Namen ziehen nach Minus, Rest nach Plus. Viel Spaß mit @yuner dem Minderleister, @Hexley.

  • Mod/RDW
    #4562 Sylvester 21 Feb. 2023, 18:20

    kann @yuner Python installieren und dann pip3 install mechanize beautifulsoup4 ausführen. Dann nehme ich die Herausforderung an.

  • #4563 Ich bin Johanna 21 Feb. 2023, 18:33

    Der Sieg fühlt sich jetzt richtig schmutzig an..

  • #4564 Richtig und wichtig. 21 Feb. 2023, 18:53

    Zitat von: Hexley

    Ich hab dafür mechanize und beautifulsoup. Beautfulsoup zum analysieren und mechanize zum Posten. Zum Glück funktioniert Xenforo auch ohne Javascript sonst wäre das damit nicht möglich gewesen.


    Iß glaa odda...

  • #4565 Shai Segal 21 Feb. 2023, 20:21

    Wenn hier scipte benutzt werden, frag' ich das nächste mal den @DerPförtner ob er mir eins basteln kann. So, da habt'ers.

  • #4566 LeFistoteles 21 Feb. 2023, 20:24

    Zitat von: Shai Segal

    Wenn hier scipte benutzt werden, frag' ich das nächste mal den @DerPförtner ob er mir eins basteln kann. So, da habt'ers.
    Dann kannst du auch gleich einfach keins benutzen. Kommt aufs gleiche raus oder?

  • Mod/RDW
    #4567 Sylvester 21 Feb. 2023, 21:21

    Ich dachte ich hätte es schon mal gepostet. Aber wahrscheinlich war das auf Drachenschanze.com

    Spoiler: Tauziehbot.py
    Python:
    import re
    import locale
    import random
    from dataclasses import dataclass
    from typing import List, Tuple, Optional
    from datetime import datetime
    from threading import Condition
    
    from mechanize import Browser, CookieJar, FormNotFoundError, HTTPError
    from bs4 import BeautifulSoup
    
    @dataclass
    class Post:
        username: str
        id: str
        url: str
        timestamp: datetime
        value: Optional[int] = None
        integrity: bool = False
        
    
    class TauziehBot(object):
    
        def __init__(self, thread_url, username, password, scan_times: Tuple[int,int]):
            '''
            scantime: min. and max. scan time, the time will be decided random for each scan between these values
            '''
            self.__username = username
            self.__password = password
    
            self.__browser = Browser()
            self.login()
    
            self.__thread_url = thread_url
            self.__min_nr_posts = 10
            self.__scan_times = scan_times
            self.__dry_run = False
    
            self.__cv = Condition()
            
            self.loop()
    
        def loop(self):
            while True:
                try:
                    posts = self.scan_thread()
                except HTTPError:
                    print('HTTPError happened')
                    continue
                TauziehBot.print_table_with_posts(posts)
                
                # check for integrity of last 3 posts
                if all([p.integrity for p in posts[-3:]]):
                    # trying to post here
                    last_post = posts[-1]
                    if last_post.username != self.__username:
                        new_value = TauziehBot.rule(self.__username, last_post.value)
                        
                        if not self.__dry_run:
                            print('Posting {} into thread'.format(new_value))
                            self.post_value(new_value)
                        else:
                            print('[DRY-RUN ] Next value would be {}'.format(new_value))
                    else:
                        print('Last post was mine, so do not post')
                else:
                    print('Do not post, because the last 3 posts seem to be fishy')
    
                with self.__cv:
                    waiting_time = random.randrange(*self.__scan_times)
                    print('Wait for {} seconds before next scan'.format(waiting_time))
                    self.__cv.wait(waiting_time)
    
    
        def scan_thread(self):
            '''scans the thread for posts, and checks for integrity, if
            the posts contain the correct values
            '''
            last_page_nr = self.get_nr_pages()
    
            posts = self.get_posts_on_page(last_page_nr)
            if len(posts) < self.__min_nr_posts:
                # in case, there's a too small amount of posts,
                # take another page
                posts = self.get_posts_on_page(last_page_nr-1) + posts
    
            last_name = None
            last_value = None
            for p in posts:
                if last_name == p.username:
                    # in case of double or multiposts, go on without changing value
                    last_name = p.username
                    continue
    
                if last_value:
                    expected_value = TauziehBot.rule(p.username, last_value)
                    if not p.value:
                        print('Post {} von {} nicht auswertbar. Erwartet: {}'.format(p.id, p.username, expected_value))
                    elif p.value != expected_value:
                        print('Erwartet: {}, von {} in {} geschrieben: {}'.format(expected_value, p.username, p.id, p.value))
    
                        # Trying to edit post, if its mine
                        if p.username == self.__username:
                            url = 'https://www.lachschon.de' + p.url.replace('show', 'edit')
                            print('Editiere Post: {}'.format(url))
                            self.edit_post(url, expected_value)
                    else:
                        p.integrity = True
    
                try:
                    last_value = p.value if p.value else expected_value
                    last_name = p.username
                except UnboundLocalError:
                    pass # just a workaround, if expected_value doesn't exist
    
            return posts
    
        def login(self):
            '''
            '''
            try:
                self.__cookieJar = CookieJar()
                self.__browser.set_cookiejar(self.__cookieJar)
                self.__browser.open('https://www.lachschon.de/site/login/')
                self.__browser.select_form(id='signup')
                self.__browser.form['login_username'] = self.__username
                self.__browser.form['login_password'] = self.__password
                self.__browser.submit()
            except FormNotFoundError:
                print('Could not login into account')
    
        def post_value(self, value):
            try:
                self.__browser.open(self.__thread_url)
                self.__browser.select_form(id="reply")
                self.__browser.form['post'] = '{:4}'.format(value)
                self.__browser.submit()
            except FormNotFoundError:
                print('Could not find reply field')
    
        def edit_post(self, url, value):
            try:
                self.__browser.open(url)
                self.__browser.select_form(id='reply')
                self.__browser.form['post'] = '{:4}'.format(value)
                self.__browser.submit()
            except FormNotFoundError:
                print('Could not find reply field')
    
        def rule(username: str, value: int):
            # Es gilt der zweite Buchstabe im Namen
            # ACEGIKMOQSUWY ziehen in Richtung -
            # BDFHJLNPRTVXZ ziehen in Richtung +
            second_char = username[1].lower()
            char_val = ord(second_char) - ord('a')
            if char_val % 2 == 0:
                return value - 1
            elif char_val % 2 == 1:
                return value + 1
            
        def get_nr_pages(self):
            '''Get the number of pages inside the thread
            '''
            response = self.__browser.open(self.__thread_url)
            content = response.read()
            soup = BeautifulSoup(content, 'lxml')
            matches = soup.select('a.link')
            pages = [ int(i.text) for i in matches ]
            last_page = 1 if len(pages) == 0 else max(pages)
            return last_page
            
        def get_posts_on_page(self, pagenr) -> List[Post]:
            '''This function returns all posts on a page in form of a tuple (username, value)
            '''
            response = self.__browser.open(self.__thread_url + '?page={}'.format(pagenr))
            string = response.read()
            soup = BeautifulSoup(string, 'lxml')
            matches = soup.select('#post-list > li')
            posts: List[Post] = [TauziehBot.extract_information_from_post(i) for i in matches]
            return posts
    
        def extract_information_from_post(post) -> Post:
            '''Retrieve all data from a post, among others:
            - username
            - time
            - url
            -
            '''
            username = post.select('.post-content > .username')[0].text
            time_str = post.select('.post-content > .date')[0].text
            
            item = post.select('.numpost > a')[0]
            post_id = item.text
            url = item['href']
    
            timestamp = datetime.strptime(time_str, '%d. %B %Y %H:%M:%S')
            value = TauziehBot.extract_value_from_post(post)
    
            return Post(username=username, value=value, id=post_id, url=url, timestamp=timestamp)
            
        def extract_value_from_post(post) -> Optional[int]:
            css_selectors = [
                '.post-content > p',
                '.post-content > .quote',
                ]
            
            # use regex, if there's one real number
            #p = re.compile(r'[+-]?\d+')
            p = re.compile(r'[+-]?\ {0,3}\d+')
            for selector in css_selectors:
                try:
                    content = post.select(selector)[-1].text.strip()
                    content = content.replace('WD_40', 'WD_vierzig')
                    content = content.replace('fischkopp85', 'fischkoppfünfundachtzig')
                    matches = p.findall(content)
    
    
                    if len(matches) != 1:
                        continue
                    matches[0].replace(' ', '')
                    value = int(matches[0])
                    return value
                except IndexError:
                    continue
                except ValueError:
                    return None
    
        def print_table_with_posts(posts: List[Post]):
            format_str = '{:8} | {:10} | {:25} | {:5} | {:6}'
            print(format_str.format('Post ID', 'Time', 'Username', 'Value', 'Integrity'))
            for p in posts:
                print(format_str.format(p.id, p.timestamp.strftime('%H:%M:%S'), p.username, p.value if p.value else 'N/A', p.integrity))
    
    
    if __name__ == '__main__':
        import getpass
    
        #username = input('Username: ')
        #password = getpass.getpass('Password: ')
    
        locale.setlocale(locale.LC_ALL, 'german')
        tauzieh_thread = 'https://www.lachschon.de/forum/thread/show/58483/'
        bot = TauziehBot(tauzieh_thread, 'Hexley', '***', (30,45))

  • #4568 LeFistoteles 21 Feb. 2023, 21:23

    Habe so schöne Erinnerungen an DS :(

  • #4569 WD_40 21 Feb. 2023, 22:16

    Zitat von: LeFistoteles

    Habe so schöne Erinnerungen an DS :(

    Halbe Buch

  • #4570 Richtig und wichtig. 22 Feb. 2023, 00:18

    Zitat von: Hexley

    Ich dachte ich hätte es schon mal gepostet. Aber wahrscheinlich war das auf Drachenschanze.com

    Python:
    import re
    import locale
    import random
    from dataclasses import dataclass
    from typing import List, Tuple, Optional
    from datetime import datetime
    from threading import Condition
    
    from mechanize import Browser, CookieJar, FormNotFoundError, HTTPError
    from bs4 import BeautifulSoup
    
    @dataclass
    class Post:
        username: str
        id: str
        url: str
        timestamp: datetime
        value: Optional[int] = None
        integrity: bool = False
       
    
    class TauziehBot(object):
    
        def __init__(self, thread_url, username, password, scan_times: Tuple[int,int]):
            '''
            scantime: min. and max. scan time, the time will be decided random for each scan between these values
            '''
            self.__username = username
            self.__password = password
    
            self.__browser = Browser()
            self.login()
    
            self.__thread_url = thread_url
            self.__min_nr_posts = 10
            self.__scan_times = scan_times
            self.__dry_run = False
    
            self.__cv = Condition()
           
            self.loop()
    
        def loop(self):
            while True:
                try:
                    posts = self.scan_thread()
                except HTTPError:
                    print('HTTPError happened')
                    continue
                TauziehBot.print_table_with_posts(posts)
               
                # check for integrity of last 3 posts
                if all([p.integrity for p in posts[-3:]]):
                    # trying to post here
                    last_post = posts[-1]
                    if last_post.username != self.__username:
                        new_value = TauziehBot.rule(self.__username, last_post.value)
                       
                        if not self.__dry_run:
                            print('Posting {} into thread'.format(new_value))
                            self.post_value(new_value)
                        else:
                            print('[DRY-RUN ] Next value would be {}'.format(new_value))
                    else:
                        print('Last post was mine, so do not post')
                else:
                    print('Do not post, because the last 3 posts seem to be fishy')
    
                with self.__cv:
                    waiting_time = random.randrange(*self.__scan_times)
                    print('Wait for {} seconds before next scan'.format(waiting_time))
                    self.__cv.wait(waiting_time)
    
    
        def scan_thread(self):
            '''scans the thread for posts, and checks for integrity, if
            the posts contain the correct values
            '''
            last_page_nr = self.get_nr_pages()
    
            posts = self.get_posts_on_page(last_page_nr)
            if len(posts) < self.__min_nr_posts:
                # in case, there's a too small amount of posts,
                # take another page
                posts = self.get_posts_on_page(last_page_nr-1) + posts
    
            last_name = None
            last_value = None
            for p in posts:
                if last_name == p.username:
                    # in case of double or multiposts, go on without changing value
                    last_name = p.username
                    continue
    
                if last_value:
                    expected_value = TauziehBot.rule(p.username, last_value)
                    if not p.value:
                        print('Post {} von {} nicht auswertbar. Erwartet: {}'.format(p.id, p.username, expected_value))
                    elif p.value != expected_value:
                        print('Erwartet: {}, von {} in {} geschrieben: {}'.format(expected_value, p.username, p.id, p.value))
    
                        # Trying to edit post, if its mine
                        if p.username == self.__username:
                            url = 'https://www.lachschon.de' + p.url.replace('show', 'edit')
                            print('Editiere Post: {}'.format(url))
                            self.edit_post(url, expected_value)
                    else:
                        p.integrity = True
    
                try:
                    last_value = p.value if p.value else expected_value
                    last_name = p.username
                except UnboundLocalError:
                    pass # just a workaround, if expected_value doesn't exist
    
            return posts
    
        def login(self):
            '''
            '''
            try:
                self.__cookieJar = CookieJar()
                self.__browser.set_cookiejar(self.__cookieJar)
                self.__browser.open('https://www.lachschon.de/site/login/')
                self.__browser.select_form(id='signup')
                self.__browser.form['login_username'] = self.__username
                self.__browser.form['login_password'] = self.__password
                self.__browser.submit()
            except FormNotFoundError:
                print('Could not login into account')
    
        def post_value(self, value):
            try:
                self.__browser.open(self.__thread_url)
                self.__browser.select_form(id="reply")
                self.__browser.form['post'] = '{:4}'.format(value)
                self.__browser.submit()
            except FormNotFoundError:
                print('Could not find reply field')
    
        def edit_post(self, url, value):
            try:
                self.__browser.open(url)
                self.__browser.select_form(id='reply')
                self.__browser.form['post'] = '{:4}'.format(value)
                self.__browser.submit()
            except FormNotFoundError:
                print('Could not find reply field')
    
        def rule(username: str, value: int):
            # Es gilt der zweite Buchstabe im Namen
            # ACEGIKMOQSUWY ziehen in Richtung -
            # BDFHJLNPRTVXZ ziehen in Richtung +
            second_char = username[1].lower()
            char_val = ord(second_char) - ord('a')
            if char_val % 2 == 0:
                return value - 1
            elif char_val % 2 == 1:
                return value + 1
           
        def get_nr_pages(self):
            '''Get the number of pages inside the thread
            '''
            response = self.__browser.open(self.__thread_url)
            content = response.read()
            soup = BeautifulSoup(content, 'lxml')
            matches = soup.select('a.link')
            pages = [ int(i.text) for i in matches ]
            last_page = 1 if len(pages) == 0 else max(pages)
            return last_page
           
        def get_posts_on_page(self, pagenr) -> List[Post]:
            '''This function returns all posts on a page in form of a tuple (username, value)
            '''
            response = self.__browser.open(self.__thread_url + '?page={}'.format(pagenr))
            string = response.read()
            soup = BeautifulSoup(string, 'lxml')
            matches = soup.select('#post-list > li')
            posts: List[Post] = [TauziehBot.extract_information_from_post(i) for i in matches]
            return posts
    
        def extract_information_from_post(post) -> Post:
            '''Retrieve all data from a post, among others:
            - username
            - time
            - url
            -
            '''
            username = post.select('.post-content > .username')[0].text
            time_str = post.select('.post-content > .date')[0].text
           
            item = post.select('.numpost > a')[0]
            post_id = item.text
            url = item['href']
    
            timestamp = datetime.strptime(time_str, '%d. %B %Y %H:%M:%S')
            value = TauziehBot.extract_value_from_post(post)
    
            return Post(username=username, value=value, id=post_id, url=url, timestamp=timestamp)
           
        def extract_value_from_post(post) -> Optional[int]:
            css_selectors = [
                '.post-content > p',
                '.post-content > .quote',
                ]
           
            # use regex, if there's one real number
            #p = re.compile(r'[+-]?\d+')
            p = re.compile(r'[+-]?\ {0,3}\d+')
            for selector in css_selectors:
                try:
                    content = post.select(selector)[-1].text.strip()
                    content = content.replace('WD_40', 'WD_vierzig')
                    content = content.replace('fischkopp85', 'fischkoppfünfundachtzig')
                    matches = p.findall(content)
    
    
                    if len(matches) != 1:
                        continue
                    matches[0].replace(' ', '')
                    value = int(matches[0])
                    return value
                except IndexError:
                    continue
                except ValueError:
                    return None
    
        def print_table_with_posts(posts: List[Post]):
            format_str = '{:8} | {:10} | {:25} | {:5} | {:6}'
            print(format_str.format('Post ID', 'Time', 'Username', 'Value', 'Integrity'))
            for p in posts:
                print(format_str.format(p.id, p.timestamp.strftime('%H:%M:%S'), p.username, p.value if p.value else 'N/A', p.integrity))
    
    
    if __name__ == '__main__':
        import getpass
    
        #username = input('Username: ')
        #password = getpass.getpass('Password: ')
    
        locale.setlocale(locale.LC_ALL, 'german')
        tauzieh_thread = 'https://www.lachschon.de/forum/thread/show/58483/'
        bot = TauziehBot(tauzieh_thread, 'Hexley', '***', (30,45))
    SAMMA WAS IS DAS CHINESISCH ODER WAS
    Es ist Deutschland hier, hör auf mit die Scheiße, das versteht doch kein Mensch.

  • Mod/RDW
    #4571 Sylvester 22 Feb. 2023, 07:54

    Zitat von: Richtig und wichtig.

    SAMMA WAS IS DAS CHINESISCH ODER WAS
    Es ist Deutschland hier, hör auf mit die Scheiße, das versteht doch kein Mensch.
    Wikipedia weiß bescheid:
    Zitat:
    Python ([ˈpʰaɪθn̩], [ˈpʰaɪθɑn], auf Deutsch auch [ˈpʰyːtɔn]) ist eine universelle, üblicherweise interpretierte, höhere Programmiersprache.[12] Sie hat den Anspruch, einen gut lesbaren, knappen Programmierstil zu fördern.[13] So werden beispielsweise Blöcke nicht durch geschweifte Klammern, sondern durch Einrückungen strukturiert.

  • #4572 Richtig und wichtig. 22 Feb. 2023, 09:17

    Du bist der, den man auf ner Party entweder gar nicht anquatscht, nur einmal versehentlich und dann nie wieder anquatscht, oder gar nicht erst trifft, richtig?

  • Mod/RDW
    #4573 Sylvester 22 Feb. 2023, 09:52

    Ich bringe auf einer Party immer viele Angus -Zitate. Seitdem werd ich nicht mehr eingeladen.

  • #4574 DJ Ötzi 22 Feb. 2023, 10:10

    Zitat von: Hexley

    kann @yuner Python installieren und dann pip3 install mechanize beautifulsoup4 ausführen. Dann nehme ich die Herausforderung an.

    3006543_460s.jpg

  • Mod/RDW
    #4575 Sylvester 22 Feb. 2023, 10:16

    Zitat von: DJ Ötzi

    3006543_460s.jpg
    Ich hab leider keine so schönen Schwerter.

  • #4576 fischkopp85 22 Feb. 2023, 15:54

    Zitat von: Hexley

    den Spaß hat man doch beim programmieren und herumtüfteln.
    Das erinnert mich an einen Online-Escaperoom, den wir während der Weihnachtsfeier zu Coronazeiten gespielt haben. Da hat einer ins Skript geschaut und die Lösungen rausgezogen... herrlich viel Spaß hatte einer von vielen lel

  • Mod/RDW
    #4577 BigBANG 22 Feb. 2023, 16:03

    Die ganze Scheisse ist nur passiert, weil man nem C++ Coder Python gezeigt hat und er nach 30 Jahren erstmals seinen Spieltrieb ausleben kann.

  • Mod/RDW
    #4578 Sylvester 22 Feb. 2023, 16:09

    Zitat von: fischkopp85

    Das erinnert mich an einen Online-Escaperoom, den wir während der Weihnachtsfeier zu Coronazeiten gespielt haben. Da hat einer ins Skript geschaut und die Lösungen rausgezogen... herrlich viel Spaß hatte einer von vielen lel
    Der Vergleich hinkt doch etwas. Ich hatte mir auch keinen speziellen Adminzugang besorgt. Jeder hätte sich ein Skript basteln können, welches das Gleiche macht. Ich hab dieses Skript auch schon mal zu Lachschonzeiten benutzt. Ich denke ich hab durch das Entwickeln und Testen des Skripts mehr gelernt, als wenn ich mich stur vor den Rechner gesetzt hätte und immer F5 gedrückt hätte nur um schnell ein paar Zahlen einzutippen.

    Zitat von: BigBANG
    Die ganze Scheisse ist nur passiert, weil man nem C++ Coder Python gezeigt hat und er nach 30 Jahren erstmals seinen Spieltrieb ausleben kann.
    Tatsächlich hab ich erst in den letzten 2-3 Jahren gemerkt, wie einfach man Programme in Python entwickeln kann. In C++ habe ich nicht mal Netzwerkfunktionen im Standard. Mit Qt kann man vieles nachrüsten was in C++ fehlt, aber der Bloat nimmt auch da überhand.

  • #4579 rauchmeddler 22 Feb. 2023, 16:11

    Zitat von: Hexley

    Der Vergleich hinkt doch etwas. Ich hatte mir auch keinen speziellen Adminzugang besorgt. Jeder hätte sich ein Skript basteln können, welches das Gleiche macht. Ich hab dieses Skript auch schon mal zu Lachschonzeiten benutzt. Ich denke ich hab durch das Entwickeln und Testen des Skripts mehr gelernt, als wenn ich mich stur vor den Rechner gesetzt hätte und immer F5 gedrückt hätte nur um schnell ein paar Zahlen einzutippen.

    Red dich nicht raus. Du bist einfach 1 Spielverderber, so siehts nämlich aus

  • Mod/RDW
    #4580 Kenny F. Powers 22 Feb. 2023, 16:49

    Zitat von: Hexley

    Ich bringe auf einer Party immer viele Angus -Zitate. Seitdem werd ich nicht mehr eingeladen.
    Die sind nur neidisch weil du mit deinem Erfolg angibst. Bestimmt so deutsche Bauern die am Fliessband arbeiten sund nach Feierabend dann ihre Happy Hour in irgendnem mexikanischen Cocktailbar Verschnitt konsumieren aber noch nie in Mexiko und dann leute beleidigen weil ihr leben so beschissen ist...