Nächstes Tauziehen: User mit x oder y im Namen ziehen nach Minus, Rest nach Plus. Viel Spaß mit @yuner dem Minderleister, @Hexley.
Tauziehen. [BEENDET]
-
#4561 WD_40 21 Feb. 2023, 18:04 -
Mod/RDW
#4562 Sylvester 21 Feb. 2023, 18:20kann @yuner Python installieren und dann
pip3 install mechanize beautifulsoup4ausführen. Dann nehme ich die Herausforderung an. -
-
#4564 Richtig und wichtig. 21 Feb. 2023, 18:53Zitat 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:21Wenn 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:24Zitat 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:21Ich 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)) -
-
#4569 WD_40 21 Feb. 2023, 22:16Zitat von: LeFistoteles
Habe so schöne Erinnerungen an DS
Halbe Buch -
#4570 Richtig und wichtig. 22 Feb. 2023, 00:18Zitat von: Hexley
Ich dachte ich hätte es schon mal gepostet. Aber wahrscheinlich war das auf Drachenschanze.comSAMMA WAS IS DAS CHINESISCH ODER WAS
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))
Es ist Deutschland hier, hör auf mit die Scheiße, das versteht doch kein Mensch. -
Mod/RDW
#4571 Sylvester 22 Feb. 2023, 07:54Zitat von: Richtig und wichtig.
SAMMA WAS IS DAS CHINESISCH ODER WASWikipedia weiß bescheid:
Es ist Deutschland hier, hör auf mit die Scheiße, das versteht doch kein Mensch.
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:17Du 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
-
-
Mod/RDW
-
#4576 fischkopp85 22 Feb. 2023, 15:54Zitat 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
-
Mod/RDW
#4578 Sylvester 22 Feb. 2023, 16:09Zitat 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 lelDer 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: BigBANGDie 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:11Zitat 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:49Zitat 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...



